This is the first post in a series that will explore the challenges of migrating a monolithic code base to a modular architecture
Post 1 – Migrating from Monolith to Modular (this post)
Post 2 – Monolith to Modular – The Extract Module Use Case
Post 3 – Monolith to Modular – Sizing and Estimating Scope
Post 4 – Monolith to Modular – Managing Violations
If you type ‘migrate monolith’ or ‘refactor monolith’ into a search bar the resulting pages of links have a significant bias towards migrating a monolith to microservices. Chris Richardson’s post Refactoring a Monolith into Microservices is a good example of the strategy based approach to migration that is suggested by a number of authors. His strategy number 3 is Extract Services.
“The third refactoring strategy is to turn existing modules within the monolith into standalone microservices. Each time you extract a module and turn it into a service, the monolith shrinks. Once you have converted enough modules, the monolith will cease to be a problem. Either it disappears entirely or it becomes small enough that it is just another service.”
The problem with this is it assumes the modules within the monolith are obvious which is more often than not, not the case. The hardest problem for many if not most organisations migrating to micro services is identifying and extracting modules.
Typing ‘monolith java 9 module’ in the search bar returns numerous links to information about Java 9 modules. But in amongst the general knowledge you will find “Modules vs. Microservices” and “Modules or Microservices”. The first is a link to the O’Reilly site and the book Java 9 Modularity by Sander Mak. The second is a link to his slide presentation in which he argues the case for Java 9 modules providing the modularity benefits inherent in microservices without the upheaval they inflict on organisation and process. He opens his presentations with a confession that he likes building monoliths then qualifies this to modular monoliths. He makes a compelling argument for the value of modular monolith over microservices. In any case, modular monolith seems to be a prerequisite of micro-services as Martin Fowler suggests in “Monolith First“.
Of course some organisations can only dream about Java 9 and microservices. Applications that are old enough to have reached their teens (or even early twenties!) are clearly core to the organisation and its business. Such applications are likely to have been under continuous development. The team will have cycled several times over the years contributing to varied styles of implementation. Whatever the architectural vision was at the start of the project it is unlikely to help today’s team understand the code. Failed architectural initiatives and incomplete technology migrations contribute to the mountain of technical debt that inevitably builds under the pressure for functional change. The code base becomes a tangle of inter-dependencies with little if any regard for interfaces and segregation. Ironically such applications probably have the most to gain from migration to a modular architecture. But where to start when “everything depends on everything else”?
Whatever the desired end state the challenge is the same; given a monolith of tangled code, how can it be transformed into a modular structure? The business will invariably prevent a Big Bang, such a transformation almost always needs to be carried out incrementally.
Which leads to a simple strategy:
Transform the monolith through incremental extraction of modules
Paraphrasing Chris Richardson:
“Each time you extract code into a module, the monolith shrinks. Once you have extracted enough modules, the monolith will cease to be a problem. Either it disappears entirely or it becomes small enough that it is just another module.”
Whether the end goal is Java 9 modules or microservices doesn’t matter. The module extraction strategy applies in either case. In fact they don’t need to be a part of the transformation. Either technology can help to enforce strong encapsulation and well defined interfaces. But that can be achieved using dependency analysis tools like Structure101 Studio and Structure101 Workspace so avoiding the added complexity of a technology migration on top of the structural one.
The Devil is in the Dependencies
Sander Mak describes the 3 tenets of modularity as strong encapsulation, well defined interfaces and explicit dependencies. The key to successful extraction of a module is making explicit the inbound and outbound dependencies of the code being extracted. Only then can they be analysed, managed and refactored to comply with the intended architecture. The degree of tangling within the code being moved doesn’t matter. It is the module level dependencies that are the focus of the extraction. Indeed, moving code from one module to another can introduce tangles into an otherwise acyclic dependency structure.
Consider the simple package structure below that is organised into a layered structure diagram. The arrows show the dependencies between the packages rolled up from the contained classes. The packages and their dependencies form a directed acyclic graph (DAG). There are no tangles in this structure.
However, extracting the package codeforextraction to a separate module creates a tangle as shown below. The dotted line indicates a dependency cycle between the modules.
This module level cycle means that the code cannot be built by a framework such as Maven. It would fail on finding the cyclic dependency during parsing of the pom.xml files. The dependencies causing the cycle need to be removed before the code can be extracted to the new module. Doing so can be a complex and time consuming refactoring exercise.
This series of posts describe how Structure101 Studio and Structure101 Workspace can be used to: –
- identify candidates for extraction
- size and scope the refactoring effort
- communicate the intended architecture to the wider team
- monitor removal of violating dependencies (and guard against new ones)
The next post uses a simple example to illustrate the approach, follow on posts will delve into more common and harder problems.