Rethinking Java Application Packaging


I’ve recently been working on a couple of new applications and wondering, why is it that even though they are greenfield is there something that makes them a bit to awkward to work with? I’m sure you’ve experienced these kinds of problem once or twice. You know there’s a class that does something but can you find it. You want to make a refactor but you can’t quite get everything to line up where you want it. Trying to reorganise the codebase but discovering that what looks nice on the whiteboard won’t work in real life because there’s this thing that will create a circular dependency.

After a bit of reflection I think that it’s our packaging strategy that’s causing these problems. I wonder if yours is similarly troublesome.

Now when I say strategy I’m at risk of aggrandisement. If you’re anything like me decisions on packaging happen on autopilot. Something that we learn through exposure to different code bases tells us to pop all the same kind of things in packages together and when a package is getting a bit cluttered in the ide, move a few things that either aren’t too important or there are lot of into a sub package. This will lead us to a code based that’s structured like this:

com.blah.awesomeapp.application
    AwesomeApplication
com.blah.awesomeapp.controller
    AlphaController
    BetaController
    DeltaController
    GammaController
    utils
        DateConversion
com.blah.awesomeapp.exceptions
    TooManyParticlesException
    InvalidAlphaSettingException
    NotAwesomeEnoughException
    InvalidTLAException
    ProcessingException
com.blah.awesomeapp.service
    AlphaService
    BetaService
    DeltaService
    GammaService
    helpers
        GammaHelper
        DeltaConstants
        BetaThingyFactory
com.blah.awesomeapp.repository
    AlphaRepository
    BetaRepository
    DeltaRepository
    GammaRepository
com.blah.awesomeapp.model
    Alpha
    BitOfAlpha
    ComponentOfBeta
    SomeOfDelta
    PartOfGamma
    Ion
    Thingy
    TLA
    OddConstant

For a long time I’ve thought nothing of this approach that I’ll be calling packaging by type in the rest of this post. It’s the way most applications I’ve worked on are structured. It has a nice benefit that you can see all the parts that make up the codebase. Knowing the types of things an application is built from gives us some good clues about its architecture. However, there is a problem when it comes to understanding an application, its a lot like having a pile of jigsaw pieces without the picture that they make up. It’s easy to work out what an individual piece is especially if they are an edge or a corner but that understanding doesn’t necessarily lead to an understanding of the whole thing. Understanding the architecture of an application is not the same as understanding how the application works and understanding how an application works is much more valuable when it comes to making changes or tracking down bugs.

But what if we took a different approach and packaged our application by functionality our example structure from above could be refactored to look like:

com.blah.awesome
    Application
    DateConversion
com.blah.awesomeapp.alpha
    Alpha
    BitOfAlpha
    AlphaController
    AlphaRepository
    InvalidAlphaSettingException
    AlphaService
com.blah.awesomeapp.beta
    Beta
    BetaController
    BetaRepository
    InvalidTLAException
    ProcessingException
    BetaService
    ComponentOfBeta
    BetaThingyFactory
    TLA
    Thingy
com.blah.awesomeapp.delta
    DeltaController
    DeltaService
    DeltaRepository
    NotAwesomeEnoughException
    DeltaConstants
    SomeOfDelta
    Ion
com.blah.awesomeapp.gamma
    GammaController
    GammaService
    GammaRepository
    TooManyParticlesException
    GammaHelper
    PartOfGamma
    OddConstant

This way all the things that make a functional slice are packaged together, making each function of the application easier to comprehend and in turn the application as a whole becomes easier to work with. If you’re unconvinced try this thought experiment, imagine that you’re writing the package-info.java for the com.blah.awesomeapp.repository package in the first example, what would it include? Now, think about what you’d write in a package-info for the com.blah.awesomeapp.alpha package in the second structure. Which of these gives the most value? Which would you rather find in source code you’ve been given to maintain?

Switch off the autopilot

So far so good but as I mentioned earlier our packaging efforts are a mixture of popping the component in the correct drawer or else hiding things that we don’t care about that much. What happens when my real world functional slice is more complex than my contrived greek letter example and I feel the need to start hiding things in a subpackage or two? At this point resist your instinct and have a think about the right place for the classes to go. I’ve three mantras that I think will help

  1. Packages are field replaceable units
  2. Package tangle is a crime
  3. Classes in subpackages should not depend on anything that’s in their parent package

Packages are field replaceable units

Take a moment come back in time with me, to a land of programming without maven or other dependency management tool, this is where I started my Java development career, rocking the servlets without stackoverflow. In those days we had to workout our own dependency trees, source and zip all the jars needed and provide a handy run command that included the classpath we wanted to use. How happy was I to discover ant and maven? The one good thing about this circus was if it turned out that there was a bug caused by a dependency you could just put a new version in the right place and restart the app and the day has been saved. I’m not advocating a return to these days but this is the origin story for my first mantra. We know that our packaging has worked out well if we could see that code being in a jar of its own that we could change the version that we were using without needing to change anything else. Hopefully, you agree that taking one of the packages from the second structure in to it’s own jar would be an easier task. What is going to make splitting the first one a problem is the amount of interdependencies between packages. This brings us nicely to my second mantra.

Package tangle is a crime

This overstatement stems from a painful time of refactoring. My team were attempting to improve our sonar quality score in order to meet a release threshold handed to us after the development had taken place. One of the biggest problems for us was package tangle, this is where packages depend on other packages which in themselves depend on the first. When packaging by type, we unconsciously adopt the if its public then we can use it rule. An inadvertently create package tangle, which is an almost invisible form of technical debt as it doesn’t cause problems until you need to make a change. In our example above, good developers keeping the code DRY will make use of the DateConversion util we created across the code base. Creating a nice package tangle, if something in the repository package makes use of the DateConversion class we’ve got a problem as it’s likely the controller package depends on the service package that depends on the repository package that depends on the controller package. In this example resolving the tangle is probably not too hard but in real life systems I’ve worked on it has been a real blocker to significant refactoring especially when the dependencies are indirect. Adopting packaging by feature is not going to stop this happening but if we adopt the packages should not use things from their parent package rule we can help avoid problems later

Packages should not use things from their parent package

This isn’t what we normally do, often the things we pop in sub packages are specialisations or implementations of what’s in the parent package, but this means that its hard to replace a subpackage. Consider the beta package from our feature based packaging example. At its current size we’re probably happy to leave it as it is but as we add more classes to the package we’ll start getting into sub-packaging territory.

com.blah.awesomeapp.beta
    Beta
    BetaController
    BetaRepository
    InvalidTLAException
    ProcessingException
    BetaService
    ComponentOfBeta
    BetaThingyFactory
    TLA
    Thingy
    Size
    MapperFactory
    BetaRepresentation
    BetaByIdRequest
    ComponentOfThingy
    ThingyThing
    BetaRepositoryMongoImpl
    TimestampUtils

One place we might start is by noticing that we have an repository interface and implementation so lets try adding a repository subpackage

com.blah.awesomeapp.beta
    repository
        BetaRepository
        BetaRepositoryMongoImpl

This is a great first start but its likely that some of our repository methods will return and accept a Beta instance so we’d need to move Beta and some of its components to that subpackage

com.blah.awesomeapp.beta
    repository
        BetaRepository
        BetaRepositoryMongoImpl
        Size
        Beta
        TLA
        ComponentOfBeta

At this point we’d need to know more about the application to decide if a Thingy is a part of a beta, if it is we should probably move its related classes into this package too. That refactoring has moved 5 -10 classes out of our main package so we’re looking in good shape At this point I’d probably move the BetaRepositoryMongoImpl into its own subpackage. Rather than going in com.blah.awesomeapp.beta.repository.mongo I’d place it in com.blah.awesomeapp.beta.mongo, this means we’re not braking the things in subpackages should not depend on things in the parent package rule and we’ll be forced to work out a good public API for our repository and related model classes. This approach hasn’t caused us any problems beta depends on beta.mongo which depends on beta.repository. All these could be in separate jars if the complexity and reuse cases stack up, there’s no package tangle and we can still keep to our rule of subpackages don’t depend on their parent but can depend on the public api of other packages.

You may think that the example below has gone to far or not far enough, I can’t quite decide but the point was to show the principles in action.

com.blah.awesomeapp.beta
    repository
        BetaRepository
        Size
        Beta
        TLA
        ComponentOfBeta
        BetaThingyFactory
        TLA
        Thingy
        Size
        ComponentOfThingy
        ThingyThing
    service
        ProcessingException
        BetaService
        InvalidTLAException
    controller
        BetaController
        BetaRepresentation
        BetaByIdRequest
    mongo
        BetaRepositoryMongoImpl

Teams discuss source control, build systems, CD/CI, which languages and frameworks to adopt, spaces over tabs, monolith or microservices, which ides will be supported, which os should be used, which methodology to follow but rarely how the code will be structured. I hope this post has given you a prompt to think about how your codebase is divided into packages and whether its serving as an aid to understanding. If it’s got the cogs whirring maybe its time to have a conversation.

comments powered by Disqus