There’s no shortage of lectures and blogposts on “Hexagonal Architecture” these days, but many of them give a much broader view of Hexagonal Architecture than it was originally presented by its author Alistar Cockburn. This can make you feel overwhelmed, so let’s re-visit Alistar's original idea and remove some of the confusion.
We’ll start the same way Alistar did, by looking at traditional Layered Architecture first.
Layered Architecture
To help ourselves deal with the complexity of software systems we can divide them into separate layers (based on their levels of abstraction). There’s no limit to the number of layers but authors usually suggest three or four. Here we’re going to use the four-layer model from the blue DDD book:
- 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 only goes downwards. In a strict approach, a layer can talk only to a layer directly below it, while in the relaxed approach a layer can talk to all layers below it in the layer stack.
Traditionally, we also 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.
A different view of a system…
In Alistar’s eyes, there is no big difference between the UI (our web client) and the Database. 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.
In this new view of a system, the Database and the Web Client are no longer back end and front end. They are both the same - outside.
In order for Core and the outside world to be able to talk to each other, we need a way of transforming data between different formats they use. For example, our web client speaks HTTP while our application speaks PHP. That is why 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 goes for our database. We need a way of transforming our PHP data structure (probably objects) into a format of our database (probably relational database). We can call this wrapper the transformer wrapper and this is where Ports and Adapters come into the story.
Ports and Adapters
Ports live in your core and they define how to talk to the Core. To do so you need to plug an adapter into them that will transform the input from the outside world format to one accepted by the port.
Going back to our application. In the 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 how we didn’t need to touch the code in the Core to support this new method of talking to our system. The Core is blissfully ignorant of the many different ways of talking to our system from the outside world (it only cares about ports).
The system can 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 where one edge of the Hexagon represents one reason of talking to the outside world. There is nothing magical about the number six — you can have a shape with n-edges.
As we already mentioned, 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 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.
Ports are divided based on how they communicate with the Core. When it comes to driving ports, they are the ones initiating the communication with the Core (driving the behavior of our application). In the case of driven ports it is the Core that initiates the communication.
And 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. Many people tend to use some layering (onion) and mix other architectures with Hexagonal. if you wish to explore more on that I recommend this article from Mattias Noback and this one from Herberto Graca.
Why?
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 in how we develop software. At the beginning of a project, when your project (domain) knowledge is at a minimum, we make big technical decisions. 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 when we’ll be able to make a more informed decision?
With Ports and Adapters, we can do just that. Say you’re starting a new project and you are not sure what kind of database you should use. You can write your repository adapters in plain PHP that will serialize your entities and save them to a text file. Sure, this solution is 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.
Swap technologies
What about changing your mind? Maybe you realized your choice was wrong, or there is simply a new better technology you want to use.
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).
Testability
When it comes to testing, Ports and Adapters architecture enables us to test our application in isolation from external dependencies.
On the driving side, we can swap our real adapters with testing adapters (our test suite), so when testing business rules in the Core, we don’t need to talk to it through the UI, we can plug in our test suit directly to the Core.
On the driven side we can swap the driven adapters with testing adapter implementations:
Don’t get me wrong. You should still write some integration tests to check if your real-infrastructure adapters work, but you don’t need to test that integration every time you’re testing some business rule in your Core.
Focus on the core
We mentioned earlier that our core is blissfully unaware of the outside world — the adapters. The only thing our core cares about is 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.
When
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. If the benefits it brings are important to you then I’d say go for it.
Top comments (0)