I would like to know if in Java (JDK 17) there is a way to easily handle classes and packages encapsulation in an hexagonal architure. I would like to make unavailable classes present in an adapter
to the domain.
To illustrate my goal, say we have this package organisation:
com.company
|-domain
|-model
|-Customer.java
|-Product.java
|-ports
|-DbPort.java
|-ServiceBusPort.java
|-services
|-CustomerService.java
|-ProductService.java
|-adapters
|-inbound
|-rest
|-CustomerRestAdapter.java
|-ProductRestAdapter.java
|-bus
|-ServiceBusAdapter.java
|-RabbitAdapter.java
|-outbound
|-db
|-entities
|-Customer.java
|-Product.java
|-repositories
|-CustomerRepository.java
|-ProductRepository.java
|-mappers
|-bus
|-dtos
|-CutomerDto.java
|-ProductDto.java
|-mappers
What I want to achieve is: all classes and packages under com.company.adapters
should not be visible from the com.company.domain
package. The goal is to prevent developers to use for example the class com.company.adapters.outbound.db.entities.Customer
in com.company.domain.services.CustomerService
. But classes inside com.company.domain
should be accessible from everywhere.
To achieve strong encapsulation with Java, you could make use of maven modules per layer, left, right and domain. I have not tried but I guess Java 9 modules would also help here. Check this link.
Another approach I use for the sake of simplicity and code readability, is to use a single module, without strong encapsulation, but different packages per layers, one for domain, another for infra..
And, to enforce architecture rules within this module, like hexagonal ones, I usually define a unit test which fails in case of any violation, for example when some domain package code directly depends on a technical API client implem defined outside the domain. So far I have used Archunit framework for that.
Also I prefer this approach because, as a developer or new joiner for example, IMO it is much easier to break some architecture rules / encapsulation patterns, not being aware till the code review, rather than breaking / ignoring a test which would fail the build, and which would also act as a spec for these rules.