It takes great metaphors to convey great ideas. And a Hexagon is not exactly one of those.
When I encountered the Hexagonal Architecture for the first time I was mind blown.
It was such a simple yet powerful architectural pattern for structuring a system.
Yet I struggled before catching its essence because of the poor choice Alistair Cockburn made to convey his idea.
In my talk Metaphor Driven Design, I explain that when the ligamen between two domains is too threadbare, when the metaphor frame is not correctly chosen, people other than the metaphor author will struggle in getting it.
This is exactly what happened with the Hexagonal Architecture.
There is a core in your system, which is the business logic. And then there are some interfaces around it. And all around again, the external environment.
While this is the key concept of the Hexagonal Architecture, Alistair based his metaphor on premises so unusual that he even felt the need to explain why he chose this name.
The hexagon is not a hexagon because the number six is important, but rather to allow the people doing the drawing to have room to insert ports and adapters as they need, not being constrained by a one-dimensional layered drawing. The term ‘’hexagonal architecture’’ comes from this visual effect.
The term “port and adapters” picks up the ‘’purposes’’ of the parts of the drawing. A port identifies a purposeful conversation. There will typically be multiple adapters for any one port, for various technologies that may plug into that port. Typically, these might include a phone answering machine, a human voice, a touch-tone phone, a graphical human interface, a test harness, a batch driver, an http interface, a direct program-to-program interface, a mock (in-memory) database, a real database (perhaps different databases for development, test, and real use).
It’s unusual for a fresh invented pattern to be published with two alternative names (Hexagonal Architecture, and Port and Adapters). I think this happened exactly because of Alistair being unable to come up with a better, unique name to represent his new idea.
So, why I’m spending time and words on metaphors?
Because this is one of the most useful architectural pattern ever, still developers struggle to capture its essence because of the cognitive dissonance brought in by those nouns.
And I want to fix it.
The core idea
Your business logic is at the center of your system.
Not spread around many places, but in one, single kernel of code which is agnostic to the rest of the world.
Around it, one or many interfaces. They are mediators between humans or external systems and the business logic.
The interfaces are completely downstream to the business logic.
It means that when something changes in the surface of the business logic, all interfaces must adapt. When something changes in the surface of the interfaces, the business logic doesn’t care.
This is the essence of the Hexagonal Architecture.
“What about the data side?” — you may ask.
I think the relation with the data side, as portrayed by Alistair, plays a minor role related to the one with the interfaces.
Choosing your own data representation is way less complicated than deal with external inputs.
This is one of the most useful architectural pattern ever, still developers struggle to capture its essence because of the cognitive dissonance brought in by those nouns.
Why should you use it
Because it focuses on the thing that matters most, namely the business.
How you interact with it is minor. The technology, the protocol is pushed at the boundaries of the system.
APIs, User Interfaces, CSV files, CLI clients. All of them are only means serving a unique purpose.
This is a great starting point if you want your system to behave consistently, because all your knowledge, all your constraints are in one place.
Let’s make an example.
You have a system for keeping record of email addresses.
Human users are supposed to interact with it through a Web Application and external systems via a HTTP API.
Before adding a new email address, you first want to check if it already exists in your system. If it exists do not add it twice.
This is a constraint of your system. This is your business logic. It’s going to be executed regardless of the interface we use to interact with it.
Hence, you should keep this constraint in a single place.
This is the kind of DRY that I like: knowledge; constraints; business logic.
Let’s say now you want to associate a person to those emails and you have two requirements:
- the person’s name must not be empty;
- the person is required to upload a profile picture;
How do you decide what goes in your business logic and what goes in an interface?
The answer is: look at which level your constraints must be applied.
In case #1, this is a constraint of your business logic. No interface should be allowed to associate a person with an empty name to an existing email address.
And you also don’t want to replicate the constraint in every interface. Again, this is what DRY is about.
In case #2, this is a constraint for humans only. Hence, it should go in the Web Application. The business logic will never know about any profile picture.
What if you want to give human users the possibility to upload their profile picture through API?
Totally legitimate. In this case, you’ll implement the Hexagonal Architecture pattern again, this time around your Web Application only.
The Hexagonal Architecture pattern is fractal.
One More Advantage
Have you ever heard about CQRS?
It’s becoming more and more popular since it came out.
Still I see a lot of developers struggling with architectural decisions when it comes to implement it.
The good news with the Hexagonal Architecture is that it naturally brings in CQRS.
Your business logic is write only. Your interfaces own a read model each and proxy commands to the business logic.
Is that simple.
“Why a read model for each interface?” — you may ask.
Because every interface serves a different user or purpose. Hence a different representation of data is required.
The read model is a decision support system.
As such, it’s only useful when it’s specifically designed for the type of user and use case to which an interface is dedicated.
That’s why I use the following heuristic: one interface for each type of user of the system.
It’s easy to build many read models if you publish your business events out into a pub/sub infrastructure.
You can let one listener from each interface pick relevant events and project them into the interface read model.
With the pub/sub messaging pattern, your Hexagonal Architecture is preserved since the business logic is still agnostic about everything around it.
It just publishes messages out. Whoever will pick them is not relevant.
I shed some light on the Hexagonal Architecture and how to tackle it in a more practical way.
I avoided giving the pattern any new name, even if the original one generates a lot of confusion. I instead focused on explaining its essence.
I also showed you how to combine it with CQRS in an extremely effective architectural combination.
There is still a lot to say about this great pattern and I left some questions unanswered:
- how the communication protocol should look like between interfaces and business logic?
- how should you internally structure the business logic?
- how different parts of the business logic communicate with each other?
I’ll show you these and more wonders in some future posts.
For now I can only say: thank you, Alistair. I’m grateful to you for this amazing knowledge gift.