
From layers to rings – hexagonal architectures explained
Estimated reading time: 4 minutes

At this year's IT restructuring seminar, armed with a pen and flipchart, I gave a short talk on hexagonal architectures. And since I believe that this architectural style is taking far too long to catch on, I would like to explain it again here.
Why layers are not enough ...
But let's start with something we are all familiar with: classic layer architecture. A user interacts with the domain layer of an application via the UI component, which in turn accesses a database via a data abstraction layer.
Firstly, this kind of architecture gives the wrong impression: the database as the foundation of software architecture – back in the day, software design started with entity-relationship modelling. On the other hand, there is a dependency between the domain layer's business code and the technical code in the DAL, as we can see from the relationships between the classes contained (of course, the diagram here is greatly simplified, with only one class per layer):
This dependency often forces us to adapt our business code even when only the technical infrastructure has changed – and this typically happens much more frequently than fundamental business changes in long-lived software systems. Conversely, however, such business developments are much easier to implement if there are no external dependencies – on technical infrastructure or anything else.
...and what you can do about it
So what does a clever software crafter who wants to decouple the domain and DAL do? He or she extracts an interface (which is always a good idea for testing and mocking):
Now, the technical code in the domain layer no longer depends on the technical implementation in the DAL class, but nothing has changed (yet) at the level of component dependencies. The Dependency Inversion Principle (DIP; Robert C. Martin explains its effect on software architectures very nicely in his blog article A Little Architecture, which is written as a fictional debate) provides a remedy here:
When reversing dependencies, it is important to ensure that the interface is not simply moved, but actually becomes part of the domain model, e.g. by naming methods according to their function rather than their technical function. In any case, the following component dependencies now arise:
Inside and outside instead of above and below
Very few software systems have no interfaces other than the user interface and database (as a persistence medium). Often, there are additional APIs to make the software's functionality available to other systems (e.g. via REST Gateway), logging in files or other storage, email notifications for certain events, and much more. If the code required for this is also organised in layers around the domain layer, taking into account the DIP where necessary, the result is an architecture similar to this:
Alistair Cockburn began visualising this type of architecture with a hexagon in the mid-1990s:
Since the six pages appeared more or less arbitrarily, they were renamed Ports and Adapters in 2005. In this nomenclature, the interfaces that have migrated into the application (domain) represent the ports, while their implementations in the outer hexagon act as adapters between the application core and users, databases, log files, external systems, etc.
Layers 2.0
If we now depart from the hexagonal representation and differentiate more finely between inside and outside, we arrive at the onion architecture proposed by Jeffrey Palermo in 2008.
It starts internally with a domain model that describes the technical components of the application without any dependencies. The domain services ring around it contains business logic that spans multiple elements of the domain model. It is solely dependent on the domain model and, together with it, maps the entire business logic. This is used by the application services, which implement application-specific logic such as access control. Finally, infrastructure, user interfaces and APIs, as well as tests, are located in a layer around it.
Dependencies always run from the outside to the inside and never the other way around. In addition to code dependencies, this also applies to data formats. For example, a character string whose format is defined by an external system should never be interpreted in a layer further inside than Infrastructure. Instead, the inner layers define formats – or rather their own data types – to which the character string is mapped by the adapter in the Infrastructure layer.
Robert C. Martin's Clean Architecture from 2012 can be seen as a derivative of the onion architecture with different designations.
The advantages of this architectural pattern can be summarised as follows:
- The business logic can be compiled, deployed and reused independently of the infrastructure.
- The application logic can be used equally by different UIs, batches, daemons/services and tests.
- The application core remains independent of the outside world.
- Persistence mechanisms can be easily exchanged depending on the deployment scenario.
- Business and technical code are consistently separated from each other.
Microservices, standalone systems and domain-driven design
The onion architecture also appears to be a natural fit for microservices and self-contained systems (SCS). Each service or SCS is implemented in the form of its own onion. From a system perspective, all other systems are part of the outside world and, as such, must not represent dependencies of the inner system layers.
In domain-driven design (DDD), each onion implements a bounded context, each with its own ubiquitous language. A context map describes their relationships. Depending on the type of relationship, the necessary translation code can be implemented as a separate onion layer, e.g. in the form of an anticorruption layer, because the above-mentioned layers can of course be extended as required. Robert C. Martin describes the implementation of the DDD concept Shared Kernel in his Clean Architecture using company-wide business logic called ‘entities’ (not to be confused with entities in DDD). The application and domain service layers contain elements that are also called services and repositories in DDD, while the domain model contains DDD entities, value objects and aggregate roots.
Conclusion
The architectural style described, which is sometimes referred to as hexagonal, ports and adapters, onion or clean architecture depending on the source, is a logical further development of layer architectures through dependency inversion. This keeps technical code in the application core independent of technical code in UIs, tests, infrastructure, etc.
Just like domain-driven design, hexagonal architectures are always a good choice when long-lived software systems with complex technical requirements need to be designed and implemented for the long term. However, the necessary investment in architecture does not seem appropriate for disposable software, prototypes, simple CRUD systems and other use cases where rapid application development is often used.