If you try researching “Hexagonal Architecture” these days, you will find a lot of articles and videos that can feel overwhelming because they often include a much broader view of Hexagonal Architecture than was originally proposed by its author Alistar Cockburn. This view often mixes Hexagonal Architecture with other architectures and approaches like Layered Architecture, CQS, CQRS, and so on.
In this article, we will take a look at the original idea of Hexagonal Architecture as proposed by Alistar in his 2005 article. Sometimes, you will also hear people using the name “Ports and Adapters” since this is the name Alistar later shifted to because he felt it is a better description of the intent of this architecture.
We’ll start the same way Alistar did, by looking at traditional Layered Architecture first.
As the name implies, Layered architecture states we should divide our systems (applications) into a number of distinct layers. Usually, three or four layers are used and here we are going to examine the 4-layered approach (as described in the blue DDD book), but the basic idea is the same no matter what number of layers you use.
Our system is divided into these four layers:
- User Interface Layer — Displays some data to end users and where end users interact with the system
- Application Layer — Orchestrates Domain objects to perform tasks required by the end users
- Domain Layer — Contains all business logic, the Entities, Events and any other object type that contains Business Logic
- Infrastructure Layer — Technical capabilities that support the layers above, e.g. persistence or messaging
Communication between the layers can only go downwards, meaning that a layer can never talk directly to any layer above it. In a strict approach, a layer can talk only to a layer directly below it, while the relaxed approach allows for a layer to talk to all layers below it.
Traditionally, we usually refer to the UI layer as the Front End of our application and the Infrastructure layer as the Back End. The “middle” part I chose to call the Core, but you could also call it the Domain, Business logic, or maybe even the Heart of Software if you wish to be more poetic.
In Alistar’s eyes, there is no big difference between the UI (our web client) and the Database. For him, a system is made of only two distinct parts: the inside and the outside. The inside is our Core and the outside is where the UI and Infrastructure live.
So, in this view of a system, our Database and our Web Client are no longer back end and front end. They are both the same, outside.
If we want our Core to be able to talk to the outside world, we need a way of formatting the input from the outside world to something our Core can understand (and vice versa). For example, our web client speaks HTTP while our application speaks PHP. So, there is another hexagon wrapping our Core.
In this wrapper, the HTTP requests are transformed into some PHP data structure that our Core can understand, and vice versa. The same thing goes for our database. We need a way of transforming our PHP data structure (probably objects) into a format of our database (often relational tables). We can call this wrapper the transformer wrapper. This is where the Ports and Adapters live.
Ports and Adapters
In the context of this architecture, Adapters match the adapter pattern as it is described in the Design Patterns book by GoF. They transform the input interface of the adaptee to some input interface the target can receive.
Let’s get back to our application. In our core, we have some Use Case. Maybe it’s a command if you are using the Command Query Separation approach or it is just a Service class with a public method. In any case, our web client speaks HTTP and our Core speaks PHP so we need to transform HTTP into PHP and vice versa. So we write our adapter that does just that. This is called a Controller.
Now we want to provide the outside world with another way of talking to our Core. Maybe with a CLI. All we have to do is to write another adapter that will take the input from the CLI and transform it into that same PHP format our Core can understand.
Notice that we didn’t need to touch the code in our Core in order to support a new communication method for the outside world to talk to our system. Our Core is blissfully ignorant of the many different ways to talk to our system from the outside world.
Our system can also be described as a Core surrounded with interfaces to the outside world. Alistar chose to visualize the Core as a Hexagon because it was easier to draw than the pentagon, but also because it gives us a nice visual analogy in which one edge of the Hexagon represents one reason to talk to the outside world. There is nothing magical about the number six — you can have a shape with n-edges.As we already said, the UI and the Infrastructure are no longer back end and front end but simply outside. So, there is a certain symmetry in this view of the system. However there is also a certain asymmetry to it, as well. What that means is that we can divide our ports into two groups: driving ports and driven ports.
The way we make this distinction is based on communication with the Core. In the case of driving ports, they are the ones that are initiating the communication with our core (driving the behavior of our application), while in the case of driven ports it is our Core that initiates the communication with them.
So this is Hexagonal/Ports And Adapters architecture as presented originally by Alistar. He gave no instructions on how you should structure the code in your Core. As I said in the introduction, many people tend to use some layering and mix other architectures with Hexagonal as well. if you wish to explore more on that I recommend this article from Mattias Noback and this one from Herberto Graca.
Using Ports and Adapters will add some complexity to your architecture and design (as any design or architectural pattern will), so let’s review what some of the main benefits of this architecture are.
Delay technical decisions
There is a paradox with our projects. Often, we make big technical choices at the beginning of our projects when our project (domain) knowledge is at a minimum. Our domain knowledge will only grow over time so wouldn’t it be nice if we could delay those technical decisions to some point in the future? Well, with Ports and Adapters we can do just that. For example, you are at the beginning of the project and you’re experimenting with designing your domain. You are not sure how it is going to turn out so you are not sure what kind of database you want to use. What you can do is write your adapters (repositories) in plain PHP, maybe just serialize your entities and store them into a text file (and vice versa). Now, this solution is definitely not production-ready, but it will allow you to model your domain and to test it, and once you are much more confident about the domain you can pick your database and write real adapters for it.
Another thing Ports and Adapters architecture makes relatively easy is to change these technology choices once we have made them. Maybe we realize our choice was wrong, or there is simply a new better technology we want to use. Well, switching between technologies is simply a matter of writing new adapters for those technologies. This, of course, can be hard work but it consists of writing new code, which is always cleaner than modifying the existing one (and why the Open-Closed Principle is such a good thing).
When it comes to testing, Ports and Adapters architecture enables us to really test our application in isolation from external dependencies. On the driving side we can swap our real adapters with testing adapters (our test suite)
And on the driven side we can swap our real adapters with mocked adapter implementations.
Focus on core
We mentioned earlier that our core is blissfully unaware of the outside world — the adapters. The only thing our core cares about are the ports that it provides to the outside world. This enables you to not be influenced by technology decisions while developing your Core, so it can be truly domain-driven instead of technology (framework) driven. Now, having you Core entirely free of any technology influence leakage is maybe a bit idealistic, but still, this architecture gives you a high level of decoupling between your domain and business logic and the technologies you use. And this is a main reason why hexagonal architecture has gained so much popularity in the Domain-Driven Design community.
So when should you go for using Ports and Adapters architecture? Well, as always, it depends. It does add some complexity to your design so you need to decide does it pay off. As a rule of thumb, I would say the more complex your domain, the more likely that it will pay off. And if you are working on a project that is meant to last for a long time, perhaps consider investing some time into making it a bit more robust from the start. The effort will probably pay off.