View More

Two more concepts which are important patterns of tactical modeling in domain driven design (DDD) are Aggregates and Modules. Both aim to organize the other building blocks of the domain model. Aggregates group multiple Entities and Value Objects together into one cohesive unit. This unit is also the transactional boundary within which we can guarantee certain invariants.

Modules are used to organize the elements of a domain model on an even higher level than aggregates. They provide a different, higher-level view on the model and code where details are hidden. Without those details in the way, cognitive load is reduced and we can reason about connections between groups of domain elements.

The Aggregate Pattern

Aggregates group together entities and value objects into a cohesive unit. The main entity of the aggregate is called an aggregate root. Clients are not allowed to access the other elements of the aggregate directly but only through the root entity. In other words, the root is the API of the aggregate.

Each aggregate denotes a transactional boundary. Within this boundary, we can guarantee certain invariants with validation logic. After every command or a series of commands that we execute on a single aggregate, we validate the state and commit the changes.

Because aggregates form the transactional boundary only objects that need to be absolutely consistent with each other should be in the same aggregate. Generally speaking, they should be kept as small as possible. Maybe we could start with one aggregate per entity. Later when we talk with the domain experts about consistency, a requirement could be discovered to combine multiple entities into one aggregate that can guarantee consistency constraints.

Sample Aggregate

Let’s look at the example of a customer relationship management (CRM) application. One requirement of the CRM is to store customer contacts. Each contact can be reached by multiple communication channels. We already decided that contacts are modeled as an entity because they need an identity. We model communication channels as value objects that don’t need a lifecycle. These two objects naturally form an aggregate where the contact entity is the root.


If we would like to create a new communication channel the classical way would be to create one and associate it with the contact. We would need to know the concrete implementation of the communication channel and how to associate it with the contact.

With the contact aggregate, we are not allowed to manipulate communication channels directly. We ask the contacts aggregate to add a new channel with primitives as parameters. We use the API of the aggregate which is free on how to create the communication channel and link it to the contact.

The Module Pattern

The domain module is a container for the other domain objects. It groups them into highly cohesive units. Objects within the module should be closely related to the domain concept it describes. Between modules, we aim for low coupling to reduce the mental load when working with its elements. By naming the modules according to the ubiquitous language, we can tell the story of the system on a higher level.

The following diagram shows a module of the CRM application. There are many other possible ways to form modules of a CRM through depending on the context and background of the domain.


In the original DDD book [Eva04] Eric Evans described modules as a way to group domain objects on the level of packages or namespaces. Nowadays many languages have a notion of module above the package or namespace structure. Therefore we have more flexibility when designing modules. We can use packages or namespaces to group objects very fine granularly and use the module language construct to model the domain modules.

On an even higher level modules are organized in bounded contexts which is the first strategic modeling pattern that I would like to introduce in the next post. When the bounded context is relatively small and contains only a few aggregates we maybe not need to split them into modules. However, in bigger contexts, they help to further divide the domain into units that we can work with.

Wrap Up / Final Thoughts

The aggregate pattern addresses some common pain points that are widely known and which I observed too in some applications. One is the overuse of getters and setters to change data that is multiple relations away. Aggregates prevent this by only allowing access to the aggregate root and restrict access to the other objects. Another point is the transactional boundary they provide. It prevents transactions over many objects which could lead to poor performance or even deadlocks.

Modules are a concept that I like very much since I first learned about them through Kirk Knoernschield’s book “Java Application Architecture” [Kno12]. They are very helpful In organizing code on a relatively high level. I’m looking forward to modularizing a domain model.

[Eva04] Eric Evans: Domain-Driven Design – Tackling Complexity in the Heart of Software (homepage)

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

View More

Welcome to this blog post series about concepts and patterns of domain driven design (DDD). Although the original book about DDD [Eva04] was written almost 15 years ago, I only recently stumbled upon an article about it on the internet [Sch17]. This read drove my interest in DDD, and I started to dig deeper, read the original book, watched some conference recordings and started to experiment.

In my opinion, DDD captures the common sense of software design and makes it explicit. I’ve seen many advanced software engineers implement the concepts and patterns of DDD which have never heard of Eric Evans and his book. That’s why I would like help to spread the word and write this series.

This posts shall give an overview of what DDD is all about. The further posts of the series will dig deeper into the concepts and patterns.

Introduction to DDD

Eric Evans does a great job defining DDD at his keynote speech at the explore DDD 2017 conference. I highly recommend you watch it. On the first slide he defines four fundamental concepts of DDD as follows:

  • Focus on the core complexity and opportunity in the domain
  • Explore models in a collaboration of domain experts and software experts
  • Write software that expresses those models explicitly
  • Speak a ubiquitous language within a bounded context

The first point is about putting the domain in the center of software and focus on it. Everything else should be built around the domain and may be replaced without affecting the core functionality. Different people have different names for an architecture like this for example “Clean Architecture”, “Onion Architecture” or “Hexagonal Architecture”. The core idea is always the same: Use dependency inversion to put the domain at the center.

The second point is about requirements and how we best gather them. It states that we should explore the models with domain experts. This advice aligns nicely with agile methodologies that recommend close interaction with the business (the domain experts).

Once we know the domain model what do we do with it? In DDD we implement it directly in code. This is possible because DDD defines patterns to build our domain model which can be implemented in code too. The model can then be used by higher-level controllers to execute use cases.

The last concept is: Speak a ubiquitous language within a bounded context. The same language should be used through conversations, the domain model and in code. The ubiquitous language allows us to communicate effectively and prevents misunderstandings. The strategic pattern “Bounded context” aims to divide software along the domain boundaries. Ubiquitous language is always valid within a given context.

The Patterns

Basic DDD patterns can be divided into two kinds of patterns: tactical and strategic. Tactical patterns are used while modeling the domain and in code. Strategic patterns are more high level and are used to structure the software on an architectural level. We will go into the details of each pattern through the series. Therefore I list them only very briefly here.

Tactical Patterns (or building blocks in original DDD terms):

  • Entity – Similar to objects in classic object-oriented design but with a focus on identity over time.
  • Value Object – Immutable entity identified by its attributes.
  • Factory – Super pattern for some of the GoF creational patterns.
  • Service – Stateless object which encapsulates domain logic that cannot reasonably put on an entity or value object.
  • Aggregate – Encapsulates multiple entities and value objects.
  • Repository – Library for obtaining access to aggregates.
  • Module – Structure objects on a higher level to reduce cognitive overload.

Strategic patterns and concepts:

  • Bounded context – Define boundaries around domain areas and reflect them in software.
  • Context Map – Model of the different bounded contexts of the system.
  • Model Integrity Patterns – Ways to model the connections between multiple bounded contexts.
  • Core Domain – Find the part of your model where the most business value lies and distill it into the highest priority context.

Supple Design

Supple design is the concept of designing the software in a way that feels natural when developing and maintaining it. The following patterns give some advice on how to achieve this property of software.

  • Intention-revealing interfaces – Give the elements of software meaningful names, use the ubiquitous language
  • Side-effect-free functions – Prefer functions that return results instead of manipulating some state
  • Assertions – State post-conditions of operations and invariants of classes explicitly
  • Conceptual Contours – Decompose elements of software into cohesive units
  • Standalone Classes – Minimize dependencies to reduce the mental load
  • Closure of operations – Where possible operations should return the same type as their argument(s)
  • Declarative design – If possible use a declarative design/program style

Don’t worry if these very short descriptions leave some question marks in your head. These are concepts that cannot simply be explained in one or two sentences. I will try to explain them in the further posts of these series. Meanwhile, if you are curious, see this article for a bit more information.

Distillation of the Core

Distillation of the core domain enables us to focus on the most valuable part of our domain model. Evans touches multiple patterns or strategies of how to approach the distillation.

  • Generic Subdomains – Extract subdomains that are not the main reason you build the system
  • Domain Vision Statement – High-level vision of the system that helps to define the core domain
  • Highlighted Core – Define which parts of model belong to the core but without extracting it (yet)
  • Cohesive Mechanisms – Identify parts of the model that form a general know mechanisms
  • Segregated Core – Remove supporting functionality from core objects
  • Abstract Core – Aim for abstract core concepts that specialized subdomains can use

Large-Scale Structure

Large-scale structure concepts organize the system on a component or layer level. It guides developers on where to find functionality and where to place a new one.

The large-scale structure is not the same as classic architecture. It is driven by the domain and should, therefore, be based on domain concepts rather than technical concerns.

  • Evolving order – Let the large-scale structure evolve over time
  • System metaphor – Look for an overarching metaphor of the system and make it explicit
  • Responsibility Layers – Organize the domain model across multiple layers
  • Knowledge level – Allow configuration of core operations from a knowledge level
  • Pluggable component framework – Abstract core with a plugin infrastructure

Wrap up / Final Thoughts

It seems that DDD consists of many concepts and patterns. However, don’t be scared, many of the overlap and together form a domain-centric way of thinking about software design.

When we take a look at our profession as a whole, DDD is concerned only with a small part of our responsibilities. Recently I read a blog post by Bob Martin where he reviews the XP process. He writes that metaphor in XP became DDD. The metaphor is only one of twelve practices in XP.

[Eva04] Eric Evans: Domain-Driven Design – Tackling Complexity in the Heart of Software (homepage)

[Sch17] M. Schimak: Domain-Driven Design? Na klar, das „blaue Buch“! – Mit „DDD“ (Domain-Driven- Design) zur gemeinsamen Sprache für Business und IT (http://www.sigs.de/public/os/2017/03/Schimak_OS_03_17_hdtz.pdf)

View More

Continuing the series about modularization patterns with Java 9 today I would like to introduce the patterns Default Implementation and Module Facade [Kno12]. As its name suggests, the Default Implementation pattern is about providing implementations for abstract modules but also default configuration. Facades are a well-known pattern from [GoF] which can be applied to modules too as we will see.

The Problem

When we try to make modules flexible, we create abstract modules that separate implementations from abstractions and configurations. These now flexible modules are harder to use because users need multiple modules (abstraction and implementation) or external configuration.

Even if there is an implementation provided for a given abstraction, users should not depend on it. We need some mechanism that wires the implementation to the user.

The Solution

The solution to the above problem is to provide default implementations of abstractions and default configuration settings. We deploy default configurations normally within the same module, default implementations can be deployed within the same module or in a separate module.

We should also provide means to obtain default implementations without requiring users to depend on them directly. With the java platform module system (JPMS) we can use services to obtain default implementations as can be seen in the following sample.

Default Implementation Sample

The sales module of our ongoing webshop sample provides an API to apply a discount to the sales. Different implementations are provided by other modules that calculate a discount based on various criteria. In many use cases, however, there won’t be a discount applied. The customer needs to pay the full amount.

As you can see, the sales module provides a default implementation of the discount calculator. It covers the case where no discount needs to be calculated. From the three services that use the sales module, two provide an implementation of the discount calculator and one doesn’t. Another case that I don’t show here and which doesn’t provide an implementation could be the test module.

Without further actions, users need to create this default implementation themselves and therefore have a dependency on it. To prevent this undesired dependency, we have at least two options: Add a factory [GoF] or provide the discount calculator as a service. Because the factory pattern is already well-known, I  choose the service option for this sample.

The following module descriptor of the sales module declares a uses dependency on the discount calculator. At the same time, it provides the default implementation as a service.

The default implementation will be used as long as no other module provides one with a higher priority. When a new requirement arises to provide a different implementation of the discount calculator, it can be implemented in a different module and plugged in the application without changing the sales module.

Default Configuration

In this post, we saw the external configuration pattern which enhances the flexibility of the module. The sample was about configuring the URL of a REST call for different environments. Every developer that wants to use that module needs to provide a configuration for it which makes it harder to use.

To improve the situation, we could add a default configuration inside the module that points at some localhost URL. Then the developers would not need to provide a new configuration each time they want to reuse the module.

Module Façade Pattern

The Module Façade pattern [Kno12] seeks to improve the usability of multiple smaller modules. When we focus on flexibility and reuse, we tend to create many small modules. Those small modules become harder to use because of their fine granularity.

A module façade provides a common API for multiple smaller modules. It may also contain configuration for them and even add functionality itself.

Sample Facade

Suppose we want to manage different product categories as independent modules. For example, we could have a home electronics module and a pc module. They both share common functionality of the product base module but add their own search and filter options.

To ease the use of those product category modules, we create a product module as a façade which includes the home electronics, pc and base modules. Users now only need to add a dependency on the façade without worrying about the different categories. When more categories are added, users automatically get access to them too without needing to modify their dependencies.

Wrap Up / Final Thoughts

When we want to use a module, it should work out of the box. If we need to find an implementation of an abstract module or need to add multiple dependencies to make it work, we are distracted from our actual task. If we design modules according to the two usability patterns default implementation and module façade, live for the users of our modules (which could be ourselves too) is greatly simplified.

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

[GoF] Design Patterns: Elements of Reusable Object-Oriented Software (wiki)

View More

In this post, we look at two usability patterns: Published Interface and External Configuration. Usability patterns help to integrate modules with each other and generally simplify development with modules.

When working with a module, the developer needs to know how he is supposed to use it. The published interface pattern acknowledges this and focuses on a clearly defined API for modules. Another problem that we may face when using modules and especially re-using them is that we may need to change some configuration values. To allow this without recompilation of the module configuration can be located outside of the module as the external configuration pattern proposes.

Published Interface

The published interface pattern [Kno12] states that one should make the API of a module well known. What is part of the public API of the module and equally (or even more) important what is not? It is important to make this obvious for anyone using your modules. If we don’t, users (like ourselves next month) will certainly access elements of the module that we did not intend to. Afterward, we cannot change such elements easily without breaking code that uses the module.

Before the java module system, it was impossible to enforce usage of the public API. With the module system, we can publish it and prevent usage of elements by only exporting the required packages and providing services.

The Sample

Continuing the webshop sample let’s have a look at the customer component before refactoring it to modules. It contains implementations for consumers and business customers. They both implement a shared interface and users should use the factory to create them. The files are organized as follows:

Without the module system, it was possible for users to create the implementation types directly without using the factory. They could access the implementation types directly. Therefore, any change to these classes could have a big impact on users of the component.

Now we refactor to a module by adding the following module descriptor:

It is now impossible for users of our module to use the implementation classes directly. We have not only published the interface by explicitly stating exported packages but also prevent access to the others.

External Configuration

Another usability pattern introduced by [Kno12] is external configuration. It states that we should make modules externally configurable. When we create modules that are configurable the chances that they can be reused are increased.

Sample

In many projects, there are multiple stages or environments involved when deploying an application. Maybe there should be at least two, one for testing and the production. To deploy a module to multiple environments it needs to be configurable. For example, the sales module from the webshop application posts an event to a REST endpoint when a sale was completed. It, therefore, needs to know the URI of the endpoint which is different for each environment. We configure the URI externally through a properties file which we deploy next to the module JAR file.

With this setup, we can provide a different settings file for each environment. It probably would be easier to store the properties files inside the module, but then we would need to rebuild it for each environment.

Wrap Up / Final Thoughts

In this post, we saw two usability patterns that simplify usage and maintainability of modules. With the published interface we can ensure that our modules are used as intended. The external configuration allows for reusable modules that can be configured for different environments or even use cases.

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

View More

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.

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.

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)

View More

While developing software in Java and other languages, we often create abstractions and separate them from the implementation. This practice has many benefits that we would like to take advantage of while designing modules. The Abstract Module and Separate Abstraction patterns from [Kno12] and API Modules from [Sand&Bak17] deal with abstractions in the context of modules.

Sample code for this post can be found on GitHub.

Abstract Module

The Abstract Module pattern states that users of modules should depend on abstractions, not implementations. When we depend on interfaces or abstract classes the implementation can change without affecting us. It may even be replaced completely by the maintainer of the module. Systems composed of modules that depend on abstractions of each other are flexible and easier to extend.

Sample

You may have come across the sample of payment methods in one form or another already. Let’s see how it looks like with Java 9 modules. If we have a webshop when we check out and complete a sale, there are multiple ways to pay. A first version of the checkout module depends directly on a credit card module and PayPal module as shown below.

As you probably imagine if we add new payment methods the checkout module needs to depend on them too. These dependencies introduce high coupling which we can avoid.

The Refactoring

To prevent coupling the checkout module to every payment method implementation we introduce a payment method abstraction. Now the modules containing payment methods depend on the checkout module.

To make the payment methods usable from within the checkout module, we design the payment method abstraction as JPMS service with the interface PaymentMethod. The payment method modules provide this service with their implementation. Finally the opus.checkout module declares that it will use the payment method services as can be seen in the simplified module descriptors below.

If we need to add more payment options, they can now easily be used by the checkout system without affecting its dependencies. It is also possible for the different implementers of payment methods to change without affecting (rebuild, deploy) the checkout module.

API Modules

The API Modules pattern [Sand&Bak17] states that we should design the API of a module carefully and create dedicated API modules. The API of a module consists of all parts that are exported from it. We should make it as small as possible to gain resilient modules.

Another Refactoring

To apply the API Modules pattern we extend the previous sample with a separate API module called opus.paymentmethod. It contains only the interfaces for using the different payment methods and exports them.

By using the JPMS service infrastructure, we don’t even need to export anything from the implementers. They have zero API and therefore no incoming dependency which allows us to change them without affecting any other module.

The API Modules pattern also states that most parts of the API should be abstractions. This statement aligns well with the goal of the Abstract Module pattern (depend on abstractions) discussed earlier. The API Modules pattern, therefore, can be seen as the inversion of the Abstract Module pattern. If modules only export abstractions other modules can depend only on them and not the implementations.

Separate Abstractions

The third pattern I would like to introduce is Separate Abstractions [Kno12]. It states that we should place abstractions and implementations in separate modules. If they live in the same module, they cannot change independently. Also, an implementation may require additional modules to provide its functionality which are not relevant for the abstraction.

Sample

Suppose our first design introduced an abstraction but located implementations in the same module. This module needs to depend on everything the implementations need like the opus.visaconnector. By refactoring the credit card implementation into its own module, we could remove this dependency from the module with the abstraction (opus.checkout). If there are many different implementations with multiple dependencies, extracting those implementations, helps to keep the modules clean an cohesive.

Where should abstractions live then? If there is only one user of the abstraction, we could place it in the same module. Otherwise, we create a separate module on which multiple users can depend.

The implementations live in their own module. This module needs to depend on the abstraction which could be problematic when it is located in the same module as the user of it. To resolve this problem, we could create a separate abstract module even if there is only one user of it. We applied this already in the second refactoring of the payment method sample.

Wrap Up / Final Thoughts

Abstractions are an integral part of software development. With modules, multiple questions arise regarding when to create them and where they should live. The patterns described in this post should help you answer those questions.

There is always a tension between (re-)usability and flexibility when designing modules. Modules that export abstractions and provide implementations too are easier to use. API only modules that separate abstractions from implementation are more flexible and have fewer dependencies but can be harder to use.

[Kno12] K. Knoernschield: Java Application Architecture – Modularity Patterns with Examples Using OSGi (homepage)
[Sand&Bak17] Sander Mak & Paul Bakker: Java 9 Modularity – Patterns and practices for developing maintainable applications (homepage)

View More

Modularization is the process of organizing code into cohesive units. This process is sometimes tedious and hard to do consistently. Patterns can help to make modularization simpler by providing solutions for some common problems. [Kno12] and [Sand&Bak17] are two books which describe patterns that are concerned with modularity. In this blog post series, I am going to show you how they can be applied using the Java Platform Module System (JPMS).

Sample code for this post can be found on GitHub.

The Pattern: Acyclic Dependencies

Dependencies between modules should never form a cycle. Different kinds of cycles include direct dependencies as well as indirect dependencies over three or more modules. In the diagram below there are two cycles, one between module 1 and 2 and the other indirectly over module 3.


When refactoring existing code to modules, it is often the case that you discover cyclic dependencies between packages that you would like to separate in different modules. However, when you refactor towards acyclic dependencies usability and reuse of the code is improved as you will see in the example below.

Implementation

The simplest way to avoid cycles is to remove dependencies altogether. So before applying any of the methods to break cycles think through the involved dependencies. Are they absolutely required or is there a way to remove them?

If they are required, there are multiple ways to break a cycle in your module dependency graph. These are escalation, demotion, and call-back. The following sections show an example of each of these methods.

The sample

Imagine a web shop application that we want to modularize. It has three components: Product, Inventory, and Sale. When a product is displayed to the user during a session, the sales page checks the inventory for its availability. According to a business requirement, when there are multiple sales going on in parallel, we need to ensure that the availability is always guaranteed. Therefore, the inventory needs to take the baskets of these sales into account when calculating the availability. Baskets are located in the sale component. These dependencies form a cycle between the sale and inventory components as shown below.

The Refactoring

When we refactor the components to modules without change we end up with the following module descriptors:

This code fails to compile because of the cycle. We need to break it by using one of the following methods.

Escalation

The first method is to use escalation. We design an additional module called sale process. This module has a dependency on both the inventory and sale module. When the system shows a product to the user, the sale process is responsible for calculating the availability. It asks the inventory for the stock count and checks the baskets from the sale module. With this change, we can remove the dependencies between sale and inventory altogether.

Demotion

Another way to break the cycle is to create a new module called basket. Both the sale and the inventory module depend on the basket module. We ask the inventory for the availability like before. The inventory, in turn, checks the baskets for ongoing sales.

This is called demotion. We demote functionality to a module on a lower level.

Callback

The third method to break cycles is to use a callback. To implement it we need a new JPMS service called OngoingSalesCalculator with a method ongoingSalesCountFor(Product product). This interface is located inside the inventory module (or a separate module) and implemented within the sale module. The inventory uses the callback to calculate the availability now instead of checking the baskets directly.

With this callback in place, we can remove the dependency from the inventory module on the sales module. By using a JPMS service, we don’t need a main class or a framework to do the wiring. We obtain the callback service directly from the service loader inside the inventory module.

When looking at the above descriptors, it may seem that there is still a cycle between those modules. There is actually one but only at runtime. The inventory module does not depend on the sales module during development and compile time but uses the calculator service only at runtime.

Wrap Up / Final Thoughts

When an existing code base is refactored into modules, the resulting dependency graph may contain cycles. These not only prevent code reuse but make it harder to think about the code and change it. There are multiple ways to break cycles, three of which we have seen in this post. When we break the cycles, individual modules can be reused and should be simpler to maintain.

The next part of this series about modularity patterns will take a look at abstractions in the context of modules. We will discover three patterns which help us design resilient modules: Abstract Module, Separate Abstraction and API Modules.

[Kno12] K. Knoernschield: Java Application Architecture – Modularity Patterns with Examples Using OSGi (homepage)
[Sand&Bak17] Sander Mak & Paul Bakker: Java 9 Modularity – Patterns and practices for developing maintainable applications (homepage)

View More

With version 9 of Java, a new module system is introduced into the language. This module system is used internally for the JDK libraries but can be used by the application code as well. In contrast to other module systems like OSGi, the Java Platform Module System (JPMS) is an integral part of the language. Because of this, I hope there will be many modularized java applications in the future.

Why should you modularize your code? This question would be a topic for a blog post in itself, but the main two reasons that stuck with me are: Organize your code in reusable units and manage dependencies. Most of the time we reuse a set of classes together instead of a single one. With modules, we have a convenient way to achieve this. Dependencies in Java are buried inside import definitions. With modules, they are explicitly stated and can be reasoned about without the help of code analysis tools.

Sample code for this post can be found on GitHub.

Module Descriptor

Let’s start with the definition of a module and its dependencies. A module is defined through the module descriptor file called module-info.java. This file must be located at the root of the source folder of the module.

The following snippet shows an example of such a module descriptor.

The descriptor defines a module named opus.address. The name must be unique and follow the same conventions as java package names. The first line inside the curly brackets states that the module exports the package com.opus.adress for use by other modules. Everything that is not explicitly exported by a module cannot be used by other modules of the application.

Dependencies

A module defines its dependencies through the requires keyword followed by the name of the module. When a module requires another one it “reads” this module. Reading a module allows the dependent module to use all public elements of packages that are exported by the read module. Java visibility rules still apply to the exported classes and interfaces.

Every module requires java.base implicitly. When a module uses other modules from the java library, it needs to read them explicitly as java.xml in the above example.

Readability is not transitive by default. Modules requiring opus.address do not automatically read opus.utils. If this is desired you need to add the transitive keyword as follows:

Every module that is requiring opus.address automatically reads opus.utils now. This is called implied readability and is transitive by itself. If opus.utils would have a transitive dependency on a third module, this would too be automatically read by modules requiring opus.address.

Services

Services are an integral part of the modules system. They provide means to decouple API and implementation and are in this sense a kind of dependency injection (DI) or inversion of control (IoC) mechanism. They can even be used in conjunction with common DI frameworks like spring or guice which is greatly explained here.

Providing a service

The following module descriptor shows the necessary parts to provide a service.

AddressRepository is the interface (API) of the service and AddressRepositoryImpl (guess what) the implementation of it. Note that the module does not export the package that contains the implementation. Therefore, the consumers of the service do know nothing about it.

Another module could too provide an AddressRepository with a CSV data source like this:

Consuming a service

Consuming a service is done through a lookup in the service registry. Some of you might know the service registry from previous versions of the JDK where it was used to provide services via metadata. In Java 9 the service registry is repurposed for use with the module system.

When a module provides a service, the module system registers it automatically within the service registry. Our code queries the registry for implementations of the service interface.

When you iterate over the returned iterator, the services are lazily instantiated for you by the service loader.

To instruct the module system to make a service available within our module, we need to declare a uses dependency on it in the module descriptor.

The uses dependencies are only resolved at runtime and don’t lead to an error when there are no repository implementations available. Our code needs to handle the situation when no implementation is available.

Wrap Up / Final Thoughts

I hope this post gave you a basic understanding of how to define modules in Java 9 and how they can help you write modularized code. If you are interested in topics like JPMS, modularity in general or domain driven design (DDD) make sure you subscribe to the opus blog newsletter. I will post a new article about every second Friday. Next up: a series about modularity patterns with samples using the JPMS.