Hexagonal Architecture (HA) is one of those "buzzwords" we hear a lot in the development world. This post tries to explain a bit what is it about and, more important, what it is not. Don't expect a thorough tour over the pattern here, but a short introduction and a reflection over what floats around it.
Mmmm... so what is Hexagonal Architecture?
It is an architectural pattern coined by Dr. Alistair Cockburn in 2005. It promotes the separation of an application internals from its interactions with the external world.
And what is not?
Many things. Probably they will appear sooner or later in this fictional conversation. In fact, as the author, I'm pretty sure they will appear :D
For now, I can tell you it's a term that has been corrupted as many others in the industry, like Devops, Microservices,or Agile. Although the term Hexagonal Architecture has been less bastardized than these other examples, there is plenty of fantasy around it.
How the pattern works?
We have our application, represented as an hexagon. Outside the hexagon there are the external actors: users interacting with a graphical interface, messaging systems, databases, vendor APIs, etc.
The application defines the ways for it to interact with external actors. Each of these contracts is a port. Ports are expressed in application language, like parts of an use case. The implementation of a port is called adapter. Adapters are interchangeable ways of interacting with the application using different technologies.
Where is the value?
HA facilitates easier to understand, easier to test, and easier to extend applications.
The isolation of the application from the outside world using ports means the application is not "polluted" with the details of external actors, reducing the cognitive load needed to understand the application logic.
The interchangeability of the adapters allows to have a test adapter for each port which can be used as replacement of the real actor, instead of forcing us to spin up the real stuff to test our application. Specially handy when the actor is a human being.
The combo of a port defining the interaction contract plus its multiple possible adapters allows to plug-in new technologies without having to modify the application. A new adapter for the new technology will do the trick.
All those three benefits are potential, though. HA is not a recipe to guarantee we don't produce a mess. But for sure the chances to do so are drastically reduced.
Why an hexagon?
The hexagon has no special meaning. It was just convenient when Cockburn came up with the pattern: easy to draw, easy to showcase the pattern, and not already taken for another recognizable diagram out there. Actually, after some time he realized a better name would be ports and adapters, but by then the hexagonal was too rooted in the community.
Should I use Hexagonal Architecture in all my applications?
There are very few universal things in software development, and no pattern is any of them. HA has its usages and they are wide, but it's not a good solution for every single case. The context of each application will determine if going hexagonal is the right approach. The benefits it provides can be irrelevant in some cases, not only for the nature of the application but also for its contextual circumstances: fixed lifetimes, tooling already in place, social aspects of the team, etc.
The pattern shines the more in the situations where many entry points for the same use case exist, which fits with most of the modern enterprise applications. Even when there are not those many entry points, the ability to use test adapters can be beneficial enough to justify applying it.
How should I arrange my code to follow Hexagonal Architecture?
Arrange it in any way you want, as long as the interactions with the external actors are done through an API defined by the application (a port).
But is not this a Clean Architecture? Should not HA tell me where to put my stuff?
Nope. HA indeed serves as an excellent base for clean architectures (note the small letters), and indeed is the base for the Clean Architecture (note the capital letters), understood as the "brand" commercialized by Robert C. Martin. But they are not the same, since Clean Architecture is an opinionated implementation of HA. The same applies for the Onion Architecture, the other popular superset of HA.
Really? And all this stuff about layers and command buses the people that does Hexagonal Architecture uses?
That's one popular way of implementing what is inside (and outside!) the hexagon, but the pattern does not enforce or forbid any of it. It just says to put a port in the application boundary when it has to interact with some external actor.
And what about Domain-Driven Design?
It's another misconception that DDD equals HA, or that one cannot exist without the other. Certainly HA is an excellent support for some of the DDD practices and patterns, but you can perfectly make use of any of them separately. Same for CQRS and Event Sourcing, in case you were about to ask about them.
I'm kinda disappointed...
Be not. Hexagonal Architecture is a powerful pattern, don't be fooled by its simplicity. As many other brilliant things, it's simple in the surface but has a fascinating depth.
I really was expecting something more concrete I could apply
That's normal. We developers like recipes we can directly use to solve our problems. It's natural that we try to narrow what gives us too much freedom into something tangible we can execute "on rails". We want solutions and we want them fast. Since HA is really lax in this sense, we have convinced ourselves that it is another different thing which guides us more strictly.
Worth to say that all these things that are usually mixed up with Hexagonal Architecture are not less valuable because they are not exactly Hexagonal Architecture.
What is the point of this post, then?
I was hoping that clarifying some of the myths around HA could help to understand that the complexity usually associated to it is actually part of other patterns and techniques that may or may not rely on HA.
We should be aware that we should not discard the benefits HA provides because we are forced to deal with this complexity.
Interesting. I'm intrigued now about this pure HA vs adorned HA
Curious way of putting it. Anyway, in following posts I will try to dig more in both.
Looking forward to it!
Latest comments (2)
Would you have some examples of situations where this pattern shines and where it does not, please? I'm having some trouble understanding what meaning you put behind "entry points" and "use case" here.
Great article anyhow !
The usual web applications we see a lot nowadays are good fits for Hexagonal. The classic example of registering a user will use the port "Register User" and this port can have an HTTP adapter to register users from the calls the client does from the web application, a filesystem adapter to register users read from a file, or a Rabbit adapter to register users from AMQP messages.
Another classic example is not using the database abstraction directly (e.g the ORM of choice), but to create interfaces (ports) inside your application that your domain code uses. For instance, the UserRepository interface represents the port and SqlUserRepository and InMemoryUserRepository are two possible adapters.
Some obvious cases where HA does not fit are scripts, libraries or agnostic frameworks. There you don't need this kind of architecture because of the nature of the code.
Another cases are already existing applications which are stable and there is no prospect for them to change, or when a CRUD application over a MVC framework does the trick.
Mostly the expected stability of the application (in the sense of the expectation of it to change and how it would change) will dictate if it is worth the effort to start or move to HA. This is highly contextual.
I hope to clarify some of this in a follow up article. With the content of this one, which is not focus on the pattern implementation, is hard to explain some things without referencing stuff that is not even mentioned.