Hexagonal Architecture is a type of software architecture which favors Domain Driven Design. This is not a post about Domain Driven Design per se but it is important to understand that the less technical issues mingle with business logic, the better. Hexagonal architecture aims at separating accidental complexity and essential complexity.
Blogging platformFor this example, I've chosen to implement a very simple blogging platform. It allows three actions.
- Write a post
- Read a post
- Share a post
The demo repository is located on GitHub
Modules of the blogging platform
- domain : domain logic. Here a very simple blogging platform.
- **persistence **: simple in-memory persistence used by the blogging platform.
- facebook-connector and **twitter-connector **: connectors to share a blog post on Facebook and Twitter
- webserver : HTTP web server for hosting the blogging platform using Ratpack
- **service-locator **: utility module for searching and exposing Java Service Providers
Modularity in a hexagonal architecture
External modules are technology-centric. Since they focus on a specific adapter, they contain usually one technology. Testing these modules independently becomes relatively easy. The only thing there is to test is the bridge between the domain ports and the technology used for the adapter. Ports are not likely to change unless business logic requires a change. This is why testing the domain can be done using mocks or using integrated solutions. The tests should remain the same even though their run context change, the former being simpler as there is no boilerplate from settings up dependencies.
Dependencies are only a means to make the domain work. Technologies of dependencies can be changed while the business logic will usually remain. Dependencies and the domain have completely different lifecycles. Trading one technology for another should have nothing to do with changing business logic. Splitting modules is a means to achieve this.
The domain module should never depend on an adapter. It can depend on utility modules like logging or data manipulation libraries.
Adapter modules should always depend on the domain module as they should implement a port of the domain module. They too can depend on utility modules. They can also have shared module dependencies if needs are.
Running the application
In a layered architecture, the outside component is usually a framework that will call business logic and lower layers. But in a hexagonal architecture, the domain is king. It should also declare how to run the application.
In Java, the class containing the
public static void main method should be in the domain. In order to run it, all modules should be on the classpath.
Using Gradle, a runtime module called
app contains no code but requires all modules at runtime and specifies the main class to launch.
In order to launch the example application, execute the following.
gradle: app run
Building this application using a proper build system can be fast as the domain module always build first and all other modules can run in parallel once their shared libraries are built. This is not a purpose of hexagonal architecture, but a rather nice benefit from having a clean architecture.
Deployment has some of the same benefits as the build. Since modules are mostly independent, they can also be deployed independently as well as having their own versioning. Each module can have its own lifecycle. In a standard Java application, the application would have to be restarted, but in a composable environment like OSGI, module swapping could be done at runtime.
To go even further, multiple applications can be architectured as hexagons and each hexagon could, therefore, talk to another through an adapter. A standard monolithic application could be split up into several parts, each having a clear specific domain. Conway's law states that
"organizations which design systems ... are constrained to produce designs which are copies of the communication structures of these organizations."
Splitting up domains and producing multiple hexagons is a way to follow Conway's law. This is important as sometimes applications are used for two different business purposes, each business has its own rules and language which can be antinomical and would make the software reflect this contradiction. Having two hexagons with a clear interface between the two helps to identify correlations between those two businesses.
Hexagonal architecture helps keep a clear separation between pure domain logic and complexity from frameworks and libraries needed. Feel free to browse and run the sample application to grasp this concept. The hexagonal architecture also favors from Conway's law in that it helps software to be more domain oriented.