The basic use case of the monolith to modular strategy is “extract a new module from a monolithic code base”

This is the second post in a series that will explore the challenges of migrating a monolithic code base to a modular architecture

Series links:
Post 1 – Migrating from Monolith to Modular
Post 2 – Monolith to Modular – The Extract Module Use Case  – (this post)
Post 3 – Monolith to Modular – Managing Violations
Post 4 – Monolith to Modular – Sizing and Estimating Scope

 

In our previous post we introduced the strategy “Transform the monolith through incremental extraction of modules”. This post describes the basic use case of this strategy – “Extract a new module from a monolithic code base”

There are a number of scenarios that this use case supports. In most cases the classes cannot be moved directly into the new module because this would result in cyclic module dependencies, which are not supported by most module technologies (Eclipse, IntelliJ, Maven, Java 9, …).

In reverse order of complexity the variants are:

  • Donor module is heavily tangled
  • Classes that will end up in extracted module are distributed across the donor packages
  • User want’s to isolate and restrict access to an existing module via an interface module, cutting off direct access to original module
    • User wants to retire existing implementation in favour of a new module
  • Extracted module is created from one or more packages that are at the bottom or top levels of packaging (no cyclic dependencies)

Any of the above are further complicated when there are multiple donor modules or where multiple new modules are to be created in a single step.

In each case the fundamental usage is the same:

  1. Use Structure101 Studio to simulate the moving of classes into the target module
  2. Use Structure101 Workspace to apply these simulations and expose violating dependencies within the IDE
  3. Refactor away the violating dependencies
  4. Actually move the classes to the target module

 

A Demonstration of Extract Module

The simplest extraction is of code that doesn’t cause a cyclic dependency when it is moved to a new module. The module code has no dependencies on anything that will remain in the monolith. The monolith will depend on the new module and the dependency structure will be a directed acyclic graph. The module can be created and the code moved without any preparatory refactoring. This reduces the use case to three steps:

  1. Use Structure101 Studio to simulate the moving of classes into the target module
  2. Use Structure101 Workspace to apply these simulations within the IDE
  3. Actually move the classes to the target module

In the example below the core module is the monolith. The diagram is a layered dependency structure wherein a package is positioned above any packages on which it depends. Selecting the nalpeiron package shows its dependencies. Note that it does not have any dependencies on any of the other packages in core. It is dependent only on external libraries (which are not visible because the Structure101 Studio project has the Hide externals property set). We would expect that this package can be moved out of core into its own module without causing a cyclic dependency.

 

 

Structure101 Studio can prove the theory by creating a virtual module and moving the nalpeiron package and its contained code into it. The new module is created via the context menu Add | Module.

 

 

The nalpeiron package is then moved (using drag and drop) into the nalpeiron module which retains the nalpeiron package structure. (Dropping packages, classes or interfaces onto a module replicates the source packaging)
It is recommended that package names are not changed when the code is extracted to the new module. This avoids changes to the import statements in code that references the moved classes which minimises the size and impact of the change set.

As expected there are no feedback dependencies from the new nalpeiron module into core.

 

 

The changes made are captured in an Action List. The list is renamed to describe its purpose and shared so it can be published to the Structure101 Repository.

 

 

The Structure Spec is updated to add the new licensing module. Any module not present in the Structure Spec appears in red (it is considered a violation if it is not in the spec). Right click on the red module and select Add to spec from the context menu. The Structure Spec will also be published to the repository.

Now publish to a Structure101 repository. In the publish dialog, make sure Publish spec and Publish Action List are both checked.

 

 

In the IDE the Structure101 Workspace properties are updated to reference the published project. When the property dialog is closed the change triggers Structure101 Workspace to perform a full refresh during which the Structure Map and the Action List are applied.

 

 

Below is the Structure Map and Action List as seen in Eclipse. The Action List is an expanded form of the list created in Structure101 Studio. This makes it clear to developers what modules and packages are to be created and which classes are to be moved. The Workspace plugin applies these actions to the Structure Map so that the classes that are to be moved are found in their intended location. Whereas the package explorer view shows the classes in their current location.

 

 

Note that the Structure Map will show module to module dependencies including dependencies on the new nalpeiron module. Hovering over nalpeiron shows that core and build-tools are dependent on it. When the new nalpeiron module has been created and the classes moved both core and build-tools will require nalpeiron adding to their project dependencies.

 

 

The developer tasked with the module extract has all the information necessary to go ahead and create the new module. In the meantime all developers can see what the architecture is going to be. Furthermore, if they were to introduce a dependency from nalpeiron into core which would violate the specified layering, this will be highlighted in the Structure Map. The diagram below shows the effect of adding a new member of type S101 into the NALPServer class. The Structure Map shows a dotted line from NALPServer to S101 to highlight the violation.

 

 

After the module is created in the IDE and the classes have been moved the package explorer location shows the classes in their new module. The entries in the Action List are greyed out to indicate they can no longer be applied.

 

 

And back in Structure101 Studio a refresh of the project brings in the changes made in the IDE. The entries in the Action List are also greyed out indicating that they are obsolete.

 

 

In this example the Action List contained a single module extraction and it could now be deleted. When multiple extractions are present in the list, the obsolete actions of a module extract that has been implemented can be deleted with Remove obsolete actions.

A similar extraction exercise can be performed on the logging package which is at the lowest level in com.headway.

 

 

We can see that the util package depends on the logging package. Once logging has been extracted into its own module, util becomes the lowest level package within com.headway and can itself be extracted to a new module.

After util is extracted, then brands and foundation become candidates for module extractions.

 

 

Extracting a module from the lowest level of the dependency structure is a low risk introduction to the extract module use case. But it may not be applicable to your code-base, which means you will have to contend with dependency violations on your first module extraction. In our next post we discuss how to manage those violations.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.