Dependency Inversion in JPMS/Jigsaw

Java 9 introduces the notorious Jigsaw (jpms) feature. It promisses a ‘better classloader’ and more formal organisation of your code into modules. Modules have explicit dependencies and code inside the module cannot be used by outsides unless opened up by marking it exported. Since Java modules are a way to abstract and manage dependecies, let’s see how one of the SOLID (OO) principles of Dependency Inversion works in the JPMS.


Dependency Inversion is achieved by letting both the suppliers (implementation) and the consumer depend on an abstraction e.g. an interface. This way the consumer doesn’t have any reference to the supplier, not even to the supplier interface. Contrast this to the regular ‘Spring Architecture’ where every Repository and Service has an interface that lives in the same project or module as the implementation.


Module Design in Java 9

Dependency inversion is a guiding principle when you design modules. To design your modules using the tried and tested SOLID principles let’s take a simple example of a client module which lists customers and their orders where the customers and orders are supplied by different suppliers.

  1. Create the interface defining the needs of your client (interface segregation)
  2. Implement the client and suppliers
  3. Link the modules

Let’s create a fictitious client that combines customers and orders and prints all orders per customer. This means it needs something that supplies customer and something that supplies orders.

The client needs repos which return orders and customers. This can be expressed in interfaces:

Look how clean that is 👏🏻 The needs are clearly defined in a set of interfaces. These interfaces stem from what the Client needs, not from what the implementation can provide. I can focus completely on my ‘client world’ without having to conform or wade through a huge API. Interfaces need to define what you need to use not describe all the stuf that anyone could use.

If your interface is completely mimics and is defined by its implementation it’s not an abstraction.

Finally the implementation could be based on a database or something but for this example we’ll just implement it used some simple dummy classes:

Lastly we’ll need something that wraps this all up and runs it. The Client needs to get a reference to the customer and order implementation and be executed. The simple repos need to be instantiated somewhere. Also let’s look at the module definition. Most notably the main app module only requires other modules, this clearly signifies its role as the starting point of the application.

A note on dependency resolution: this can be achieved in two ways: via the ServiceLoader mechanism (a native Java feature) and through plain Java, often implemented using an IoC library like Spring or Guice. In this case we just inject the object using plain code since it’s a simple example app.

Dependency Inversion in action

In the module-info.java files it’s clear to see the ‘requires client.needs’ module in both the the repo and the client. The most important consequence of following dependency inversion inside Java modules is that the client doesn’t require the repo module.

This is contrary to how Java itself has organised the JDK modules. Hiding the private packages of your module is only one part of good design. If you’re a library provider (e.g. apache commons or hibernate) you will use the default module exports to signify clearly what are the parts to be used on the outside and hide what no one needs to use. But you’re probably not building a public library.

When you’re building an application most of your code (e.g. your CustomerRepositoryImpl) will be standalone and unique within your architecture, not exposed to the outside world. This means you’re free to fully use the SOLID principles and your code should depend on the interfaces of those needing its services.

Closing Thoughts

Module design has become more of a hot topic. Partly because of Java 9 but also because of discissions on software design triggered by the microservice popularity. If you’re building an application it often needs to be modular to keep it manageable. Defining modules isn’t simply a matter of collecting all the classes with a similar name. It requires thought and clear principles. I’m hoping the SOLID principles will help you define a clean architecture and module design!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade