Package by Component with Clean Modules in Java

Software architecting is about tradeoffs. Even when the theory is good the implementation details can break it. In this article I try to find the best from two architectural approaches: Package by component and Clean architecture (a variety of Ports and adapters).

Package by component, as proposed by Simon Brown in his Missing chapter, is an architectural approach organizing code based by bundling together everything related to a component. If done properly (only the component entry point is marked as public) enforces architectural rules, like not bypassing the busines logic in the infrastructure layer - problematic in the Ports and adaptes -, using only the standard Java compiler mechanism.

Enforced Separation of Concepts

In my experience it is very important to have such a strict mechanism, because otherwise the temptation to skip the rules is too strong, especially when working under time pressure (don't forget - the stress-driven architecture is the big ball of mud).

Package by component

Package by component hides code under a package and so makes them inaccessible from another packages (components). There is no way to access the OrderRepository from the OrderController anymore and that's great.

The problem here is that there is no mechanism to prevent the access the OrderRepositoryJdbc (infrastructure) from the OrderServiceImpl (domain). The ease of using the implementation directly instead of the interface is still too high. It seems even in the Package by component approach is the separation of concepts enforced not enough.

Modules to Rescue

Fortunately we have more than only Java packages - we can configure separate modules or projects in a build tool (eg. Maven, Gradle, ...). We can use these artifacts as dependencies and hide so the "outside" from the "inside".

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 architecture, but this time the domain and infrastructure parts are both separated into modules. Because the domain artifact has no dependencies to the infrastructure artifact, accessing the OrderRepositoryJdbc from the OderServiceImpl would now cause a compilation error.

We don't have to stick with only two modules and can create a finer structure, for example modules like web, database, etc.

Working Example

We extend the "MyShop" application shown in the pictures above. We create a simple web application with products to add into a shopping cart and then to order.

As a glue we use the Spring framework (context) and for the web part the Spring Web/MVC all together with the Spring Boot.

Package by component with clean modules

Modules creates an onion structure:

Clean modules - MyShop

Another benefit of using modules is that we can get rid of unnecessary dependencies in a declarative way. For example, if we want to add another implementation of the OrderRepository, like OrderRepositoryInmem, we can do it in two ways: 1. adding a new class into the db module, 2. creating two separate modules db-jdbc and db-inmem. Then we can exclude the dependencies in the spring module, or just declare one of them respectively. With the second approach the implementation can be easily changed just by adding/removing a dependency, on the other hand the maintenance of an additional module is needed and in some cases this could lead to an explosion of modules.

The source code could be found on GitHub.

Note on the implementation: The purpose of the code is to show the Package by component with modules approach, nevertheless it is not a perfect Domain-driven design example. Following the DDD principles seriously, we should see a package com.ttulka.myshop.cart, classes like ShoppingCart, OrderItem, proper factories for domain objects and, of course, tests!

Happy architecting!