Skip to content

Modularity Patterns with JPMS: Independent Deployment

The next pattern that I would like to discuss is called “Independent Deployment” [Kno12]. It states that modules should be independently deployable units. For a module to be independently deployable, it cannot have any outgoing dependencies on other modules. In most cases, this is unrealistic, but the dependencies can be minimized.

Why should modules be independently deployable? If we want to reuse a module, every outgoing dependency is needed too. Therefore if we minimize outgoing dependencies, the module is going to be simpler to reuse.

Some dependencies are “lighter” than others and are preferable to “heavier” ones. For example, abstract classes and interfaces are light because they have a lower rate of change than their concrete implementations. A dependency on implementation can be avoided by defining an abstraction and use a wiring technic to get access to the implementation. With the java platform module system (JPMS) we can use services to implement the wiring.

Sample code for this post can be found on GitHub.

The Sample

In the first part of this series, I introduced the web shop as a sample of an application that we would like to modularize. Now we have the following modules in place.

module opus.inventory {
  exports com.opus.inventory;

  requires com.opus.basket;
  requires opus.product;
}

module opus.basket {
  exports com.opus.basket;

  requires opus.product;
}

The inventory module depends on baskets to calculate availability. Now imagine if we would like to reuse the inventory module in an application for inventory management where there are no sales with baskets going on. With this dependency in place, we would need to bring along the baskets module too, but there would never be any baskets.

The Implementation

The solution is to define an abstraction for availability adjustments. We create a new interface called AvailabilityAdjuster in the inventory module which is implemented by the basket module. Then we need to wire the implementation from the basket module to the inventory using a JPMS service. The inventory module now only depends on the abstract service interface which could be deployed within the inventory module itself or in a separate module.

Module opus.inventory {
  exports com.opus.inventory;

  requires opus.product;

  uses com.opus.inventory.AvailabilityAdjuster;
}

module opus.baskets {
  exports com.opus.basket;

  requires opus.product;
  requires opus.inventory;

  provides com.opus.inventory.AvailabilityAdjuster
    with com.opus.basket.adjuster.AvailabilityAdjusterImpl
}

This design allows us to deploy the inventory module independently. It is a bit more complex than the previous that allowed direct usage of the baskets module through. That’s a typical tension that we encounter in module design: To get the benefits of reusability, the use of some modules maybe gets harder. Therefore, I would aim for the simpler solution first. When the need arises to reuse some module, there is always the possibility to refactor.

Another Option

The sample above uses a callback to break (or invert actually) the dependency from the inventory to the baskets module. There is another possibility that may be better in some situations. We could implement escalation to break the dependency. As you may remember from the first part of this series the availability is used by the sales module that displays it to the user. If we move the responsibility to adjust availability according to the contents of the baskets to the sales module the dependency from inventory to baskets can be removed.

With this design, we move functionality to another module. The internal inventory system could implement the same concept of availability through, may be based on what is going to be delivered through the day. Then it may be undesirable to implement two solutions.

Wrap Up / Final Thoughts

Modules should simplify reuse of code. Therefore it should be possible to take individual modules and use them independently. That’s the goal of the independent deployment pattern. It advises how to design or adjust modules to make them reusable.

The JPMS enables us to remove dependencies by converting them into services which can be made optional to use or provided by a simple implementation. In our example, if the inventory system defines no availability adjusters, we can ignore this functionality and still reuse the module.

[Kno12] K. Knoernschield: Java Application Architecture – Modularity Patterns with Examples Using OSGi (homepage)

An den Anfang scrollen