A Common Problem
We've all come across 'that' application. The one nobody wants to touch. The one with the spaghetti code. The one that's so hard to understand that making any changes becomes an exercise in managing risk!
It's such a common problem. Every company I've worked for has had applications just like this. Even with the best developers, without a clear structure to work within, bits can get 'bolted on' here and there – and before we realise it, we're in spaghetti hell again!
So, how can we structure our applications to avoid this problem?
Ports and Adapters Pattern
One approach is to use the 'ports and adapters' or 'hexagonal architecture' pattern. See link here. The idea of this approach is to isolate any technology-specific code responsible for talking to an integration point (for example, a database or web service) into a self-contained module called an adapter. So if we want to swap out one integration point for another (in the future), it will be straightforward: the adapter exposes an interface (the port), so as long as this interface doesn't change, the rest of your code will not need to know if you have swapped one adapter for another.
I have worked with an application in the past where we had code scattered throughout the codebase that talked to a source of indexed data. The day came where we needed to swap out this data source. But because the code was scattered everywhere, it was a massive undertaking to make the change. And it should have been simple!
So the 'ports and adapters' pattern is a good start to helping structure code tidily. However, we can do even better.
For convenience, below is a diagram lifted from the article linked to above. Notice our well-behaved adapters isolating technology-specific code nicely from the rest of the codebase:
The challenge here is the big Application bit in the middle. The adapters have introduced some helpful structure, but what about the structure of the Application? Are we not still at risk of this becoming large, complex and unwieldy spaghetti? The next stop on this journey is into the world of modular codebases...
A 'module' operates like a mini project within your project. In fact, Eclipse actually calls them projects. Personally, I prefer IntelliJ, which uses the term 'module'. Here are a few diagrams to aid visualisation of a modular codebase:
High-level view shows modules ‘web’, ‘services’, ‘adapter-email’, ‘adapter-mysql’, ‘adapter-solr’ and ‘model’, all at the project root:
Each module has the familiar structure:
*Module dependencies (discussed more later) are defined in the pom.xml file of each module. Here we see an excerpt from the pom.xml of the web module, with a dependency declared on the services module: *
For more information on using modules in your codebase, I would recommend here as a good starting point.
With our modular codebase, we will follow the 'ports and adapters' pattern (where each adapter is a module), but we'll introduce some further structure to the application. It might vary how you want to go about this. In my experience, the following set of modules works really well:
A further advantage of this modular approach (which is not so immediately obvious) comes from the fact that there is a dependency between modules. Importantly, this dependency only works in one direction, as shown above. What's great about this is, for example, the orchestration layer can speak to services, but services cannot speak to the orchestration layer. Services can speak to adapters, but adapters can't speak to services: our code is forced to follow a clear pattern of dependencies that encourages keeping things simple. It helps us avoid spaghetti code!
In a previous company, we used this approach for a big project - one that really needed to be done well. The developers loved working within this structure. They felt it aided simplicity and, as such, productivity too. The project went live on time, and guess what - no spaghetti!