Migrating From Monolith to Modular — Part 1
Migrating From Monolith to Modular — Part 1
This series exploring the challenges of migrating from a monolithic to a modular architecture like microservices starts with an overview of the problem: identifying the modules.
Join the DZone community and get the full member experience.Join For Free
Containerized Microservices require new monitoring. Read the eBook that explores why a new APM approach is needed to even see containerized applications.
This is the first post in a series that will explore the challenges of migrating a monolithic code base to a modular architecture.
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, organizations migrating to microservices 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 organization 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 organizations 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 organization 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, 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 analyzed, 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 organized 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 describes 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.
Published at DZone with permission of Mike Swainston-Rainford . See the original article here.
Opinions expressed by DZone contributors are their own.