Reputation: 13
I am currently in the process of implementing a simple hexagonal architecture. I have followed the DDD principle and placed the DomainModels alongside the DomainServices in a domain-package. In addition to this, there is the adapter-package, which contains inbound and outbound adapters.
Now, the ports also need to be placed somewhere. Initially, I put them in a third package called application and added this path to the ApplicationService method from ArchUnit. However, this is a problem for ArchUnit because there are now accesses from the domain-package to the application package:
Class <...domain.services...> implements interface <...application.ports.in...> in (...)
Therefore, I have now tried placing the ports in the domain-package, which achieves the desired effect. However, I am now wondering what the application package or the applicationService method is needed for? What should go in there? Or am I misunderstanding something here?
Here is a brief overview of my (final) structure again:
- adapter
|- inbound
|- outbound
- domain
|- model
|- services
|- ports
- application
|- ???
- Application.kt
And here is my current test:
val `hexagonal architecture is respected`: ArchRule = onionArchitecture()
.domainModels("$rootPackage.domain.model..")
.domainServices("$rootPackage.domain.services..", "$rootPackage.domain.ports..")
.applicationServices("$rootPackage.application.." )
.adapter("Adapter", "$rootPackage.adapter..")
.withOptionalLayers(true)
Upvotes: 0
Views: 144
Reputation: 469
Both ports (left/right, API/SPI, inbound/outbound) must be placed in the domain... the API port is your use case and the SPI port is the port implemented by the right adapter (providers...). The term 'application' refers to an anti corruption layer (ACL: see tactical DDD).
You must imagine the domain as an independent box that can be tested independently of the other layers. To be e2e tested, it exposes left ports (use cases) and uses right ports (SPI) to achieve features.
Furthermore, your file structure looks like a technical breakdown. I think it would be better to use a functional structure and divide according to your bounded contexts (see strategic DDD). After that, inside each bounded context or even inside each functionality, you could have a technical structure (entity, service...).
Upvotes: 0