Package by Component with Clean Modules in Java

Let's find the best combination of two good architectural approaches: Package by component and Clean architecture.

Software architecture is about tradeoffs. Even when the theory is good the implementation details can break it.

Package by component, as proposed by Simon Brown in his Missing chapter, is an architectural approach to organizing code based on bundling together everything related to a component. If done properly, it enforces architectural rules, such as not-bypassing the business logic in the infrastructure layer, by using only standard Java compiler mechanisms.

Enforced Separation of Concepts

In my experience, the absence of a strict mechanism like this will lead to skipping the rules eventually, especially when working under time pressure. Such a stress-driven architecture results in the unfamous Big ball of mud.

Package by component
Package by component

Package by component hides code private for the component under the package accessor and exposes API code with the public accessor. This makes details inaccessible from other components that don’t share the same package. In the example above, there is no way OrderController could access OrderRepository, which is exactly what we want.

Unfortunately, there is still no reliable mechanism to prevent OrderRepositoryJdbc infrastructure adapter from accessing OrderServiceImpl domain object directly. The obvious ease of using the implementation directly is very tempting. It seems that in the Package by component approach is enforcement of concepts separation not sufficient.

Modules to the Rescue

Fortunately, Java packages are not the only mechanism to separate concepts on the level of code. Build tools such as Maven and Gradle comes with their own mechanisms for creating code modules (projects). The direction of the artifact dependencies defines the separation of the outside from the inside explicitly.

Package by component with modules
Package by component with modules

As you can see in the picture, there are no changes in the packages structure. We’re still following the Package by component approach, but this time the domain and infrastructure parts are both separated into individual modules. Now, the domain artifacts has no dependencies on any infrastructure artifacts which makes accessing OrderRepositoryJdbc from OderServiceImpl cause a compilation error.

We don’t have to stick with two modules only. A finer modules structure like web, db, and similar would be plausible.

Working Example

As a non-trivila example, we create a simple web application with a product catalog, a shopping cart and an ordering process.

Spring framework is used to implement the web part and Spring Boot to glue all components together.

Package by component with clean modules
Package by component with clean modules

Modules create the familiar onion structure:

Clean onion structure
Clean onion structure

One more benefit emerges. With modules we can get rid of unnecessary dependencies in a declarative way. For example, if we want to add another implementation of the OrderRepository interface, such as OrderRepositoryJpa, we can split the db module into two separate modules db-jdbc and db-jpa. Then, the implementation can be easily changed just by adding or removing dependencies. Spring Boot starters use this approach very successfully.

Warning: Albeit clean modules are nice, they required additional maintenance. In extreme cases, this could lead to an explosion of modules and rapid increase of development costs. Therefore, use with caution!

The source code could be found on GitHub.

Implementation note: The purpose of the example code is to show the Package by component with modules approach. Nevertheless, it is not a perfect Domain-driven design code. Following the DDD principles seriously, we should see packages such as com.ttulka.myshop.cart, classes such as ShoppingCart, OrderItem, and, of course, tests!

Happy packaging!