3

At our company, we maintain three products, which are quite similar, in 2 Projects.
All Java, using Maven for dependencies.

These three products evolved out of another project, which is now a maven dependency of the two Projects.

Let's depict it like this: enter image description here

Project A and B are so similar, that essentially every bug fix for A has to be copied or merged for project B.
The Flavors of Project B, on the other hand, describe very distinct differences in behaviour between these two. For example other items in a right click menu. This had been accomplished by very ugly if-else-monsters, which have partly vanished in favor of a more OOP like approach, but now lead to another problem: where to organize these flavor dependent classes?

At the time being, we deploy our projects with profiles for the different maven builds.

Our goal is to merge both Projects (possibly also with the base project) into one with distinct flavors.

My Question is now: how to Organize this?
Our main approach is, to enhance Project B with the features of Project A by defining a third flavor for Project B and let A gradually be superfluous, so that our bug fixes have to be done in only one place.
So We imageined our Project structure roughly like this:

tld.company.project.<all of the logics with dependency injection> 
tld.company.project.flavor.<flavor 1>.<implementations>
tld.company.project.flavor.<flavor 2>.<implementations>
tld.company.project.flavor.<flavor 3>.<implementations>

Inside of the business logic, we would then inject one XyHandler, which is implemented in every flavor and will be populated by our beans.xml

Does this seem to be maintainable? Or even desirable? Every idea is welcome!

The common codebase is just that huge, we have to find a way to combine those two Projects.

2

Code that is common to A and B should be moved to the base project. If anything is left in A after doing that move it to flavor 3. When you're done B should only have flavors in it.

It's really that simple unless there is something you've left out. Remember, projects exist for conceptual separation more than anything else. If having multiple projects is a problem merge them. If you keep them, just make what each is for very clear.

Alternatively you can do something I'm more used to. Each product gets its own project. Base gets everything common.

The real trick is getting people to stop duplicating work across projects. Figure out who and why this is happening and be sure it stops.

  • I think you are right. We already have a shared project, so why not use it further? Good point, thank you! – Phe0nix Jun 2 '16 at 6:01
  • 1
    Here are some rules that can help. If anyone writes code outside base project and it turns out that it's needed in more than one product that person gets to move it to the base project. If anyone codes something in the base that can only be used by one product, that person get's to move it to the child project. People will learn to plan their placement well or how to move code quickly. And either one is fine. – candied_orange Jun 2 '16 at 7:11
  • Code stalking can also help. When someone contributes code give it a read and ask yourself if it's in the right place. If they didn't put it in the base and it's needed in another project get after them. You can backup your claim by writing the code that will use it in the different project. If they put it the base but it's only used once see if you can write the code that will use it in the different project. If you can't go ask them what they're doing. Best way to make people care about this is to care about this. – candied_orange Jun 2 '16 at 7:47
2

I would consider defining a flavor by a configuration file rather then a collection of interfaces.

I would imagine it being something like this:

features:
   - FOOBAR
   - GOATS
   - HOUSES
frobnosticator_mode: FLASHY
server_address: www.example.com
name: "Flavor Fantastic"
length_of_music: 2012
supports_cows: True

Whenever your code needs to behave differently for different flavors, it should read the relevant piece of information from the configuration. For example, when the user right-clicks on an item, the code decides which items to display in the menu depending on which features were enabled. The dependency injection can inject a different implementation of Frobnosticator depending on the value of frobnosticator_mode. When the user tries to import a cow, the app can check the supports_cow configuration.

But why?

  1. The same information can be used in multiple places in your code. If two pieces of functionality depend on whether a feature is enabled, they can simply read the same configuration value.
  2. It will be easier to enable functionality in a flavor that already exists in another flavor.
  3. Each piece of code will clearly show what particular aspects of a flavor it cares about by what configuration values it reads.
  4. Code will be easier to follow if it doesn't constantly have to jump to one of three subclasses
  5. Applies pressure to have the same well-tested code running in each flavor instead of different less well-tested code running each.
  • I think @CandiedOrange nailed it pretty much, to get the common logic into one repo. But yours seems to be a great approach in the long run, because we will still need to configure the exact behaviours. Thanks! – Phe0nix Jun 2 '16 at 6:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.