Hexagonal Architecture meets Maven
Know your patterns, so you don't re-invent the wheel. Patterns are like LEGO: You can combine them to larger patterns. One of those is the Hexagonal Achitecture officially called:
Ports and Adapters
proposed by Alistair Cockburn, one of the co-signers of the Agile Manifesto.
I've written about his work before and can highly recommend his latest book "Hexagonal Architecture Explained" (or for that matter any of his writings).
In a nushell, Ports and Adapters proposes an approach that leads to loosely coupled systems, that separates business logic from the software environment (think I/O, databses, API and UI) to facilitate clarity and ease of testing.
This post doesn't aim to explain it, for that you can refer to the Wikipedia article or, better, read the book but trying to describe how to apply it to Java in a Maven project.
Context
In chapter 3 of the book Alistair provides code samples, but without file name or locations, while in chapter 3.3 pointing to the Bluezone example, a Java example contributed by his co-author, the late Juan Manuel Garrido de Paz.
I liked the code, I didn't like the maven structure, since it deviates quite a bit from a standard maven project structure. This isn't just a matter of taste, but complexity. All maven plugins presume, unless told otherwise, a specific structure. Reconfiguring each of them is unneccesary work when following the standard layout. In German we would say: "Man bürstet eine Katze nicht gegen den Strich".
So I devised a simple example, trying to be more in line with maven expectations.
Constrains
In the hexagonal architecture we have ports, expressed in Java as interfaces, the app using driving and driven ports and the adapters to be able to adjust ins and outs of the application with its surroundings as necessary.
A concrete implementation of a port must not depend on the app or adapter, the app must not depend on a concrete implementation, but only the interfaces.
Maven has an expected directory structure, with dependencies defined by their pom.xml
(and availability in Maven central or any reachable registry). To simplify the example, I used an aggregator pom which eliminates the need of uploading the individual modules to the registry for the example.
Another detail: the maven structure sharply distinguishes between application code (src/main
) and test code ( src/test
). While you can reconfigure all of this, I'd like to save you gentle reader from that pain.
Project layout
@startwbs
*: <&flag> Project root
pom.xml;
**: <&globe> module adapters
pom.xml;
*** src/main/java
*** src/test/java
**: <&globe> module app
pom.xml;
*** src/main/java
*** src/test/java
**: <&globe> module ports
pom.xml;
*** src/main/java
@endwbs
Class & package structure
The architecture destinguishes between primary or driving ports and secondary or driven ports. Primsry ports are the ones implemented by the app, while secondary ports are the interfaces the app interacts with, provided by some form of dependency injection. Thus the ports have two packages:
package com.notessensei.primaryports;
package com.notessensei.secondaryports;
The tests for the application, including the mock objects implementing the ports, live with the app in package com.notessensei.app;
separated in main
and test
directories, but featuring the same package hierarchy.
Variations
- Change
primaryports
todrivingports
and/orsecondaryports
todrivenports
- Reflect the "portness" in the package hierachy:
package com.notessensei.ports.driving
Conclusion
- Maven and Hexagonal Architecture go well together.
- Sticking with the default Maven directory layout works .
- Maven modules are a suitable tool to separate dependencies.
- Keeping
main
andtest
apart works well with default settings for test automation likemvn test
Go check it out, as usual: YMMV
Posted by Stephan H Wissel on 24 July 2024 | Comments (0) | categories: Java Maven WebDevelopment