Hexagonal architecture made simple – theory

Tomasz 0
rule, hook, check mark-1752625.jpg

Same thing, multiple names

Hexagonal architecture, clean architecture, port and adapters architecture (aka port&adapters), onion architecture (maybe there is more) they all refer to the same patter. As far as I know the first one who came up with the idea was Alistair Cockburn calling it Hexagonal Architecture. It’s not new as we can see. Through out the years the concept was discovered or reinvented by others software engineers. I have the impression that over the past few years this concept has been booming. Every year we can see someone on programming conferences who talk about it. Therefore, a lot of possible implementations come into radar. Some of them are indeed clean but others seems to be understandable only for authors.

Short recap what is hexagonal architecture all about

The heart of the solution is domain model. According the pattern in application, it is essential to keep business logic distinct from infrastructure and framework code, maintaining complete autonomy. This separation not only facilitates the interchangeability of infrastructural components without impacting the business domain but also simplifies code comprehension, ensuring that the business intent remains clear and uncluttered by technical specifics.

It turns out that clean architecture has a lot in common with Domain Driven Design (DDD for short). In both cases, the heart of the solution is the domain and the rules within it. All the external components with which our application coexists are just a technical detail. These similarities often have a very big impact on the later implementation.

When we should apply hexagonal architecture?

Unfortunately, the answer to this question is not so simple.
First of all, we can answer when we definitely should not use the hexagonal/clean architecture approach, namely: if your service or application is a simple CRUD application, just don’t go that way! In other words, if your system is based on accepting some kind of json from the controller layer, basic data validation and writing to the database, we are certainly dealing with a situation where hexagonal architecture will cause more trouble than it solves.

So, one could say that hexagonal architecture is suitable for complex systems, with a rich domain and many business rules. Of course, this is true. However…

Hexagonal architecture challenges

However, reality presents further challenges. Most often, at the start of a project, we don’t know exactly how our system will look like. Little by little, we build it by adding more bricks to our building. There are exceptions to this rule. Most often we know the system perfectly well when:

  1. We are writing it for the second time,
  2. We are experts in a particular domain

There are techniques such as event storming helping us determine what kind of system we are dealing with and discover unknown domains. However, if we are working in a startup, most often we need to quickly build an MVP, and if it turns out to be successful we add new functionalities, and leave a refactoring “for Christmas task”.

We have to be aware that there is nothing for free (except God’s grace) in our life. The same rule applies to hexagonal architecture. The reality shows that the more ports and adapters we have the more mapping we have to implement. It becomes uncomfortable and irritating when our domain model is fat and exactly the same as:

  • Database model
  • Rest API model
  • Message broker model
  • Cache model

In addition, you should also consider whether it makes sense in our case to have completely different domain objects and entities for the database. One of the arguments that emphasizing the purpose is that if we change the database engine or database framework the migration will be painless, and our domain will remain untouched. This is absolutely true, but how often do we make such changes in our projects? Do we actually migrate all parts of the system or do we only do it in new microservices? In the past decade I have only participated in one such migration it was from Hibernate to JOOQ.

What is wrong with layered architecture?

We might have thought that there must be something wrong with layered architecture, since software engineers have decided to introduce a new architecture style. From my experience and observations, the biggest problem with layered architecture is database driven design. Moreover, very often this architecture is combined with the so-called anemic domain model, and all the business logic is pushed to the service layer. As if that wasn’t enough, the code inside services is procedural and leads to the creation of so-called god classes, sometimes having 8,000 lines of code.
Additional problems I described earlier:
https://javacaptain.dev/2021/03/16/its-2023-and-object-oriented-programming-is-dead/
https://javacaptain.dev/2021/03/18/forgotten-art-of-package-private-scope-in-java/
Ultimately, we end up with code that is hard to test, hard to maintain with high coupling and low cohesion between components.

How to find the right balance?

Very often in our projects we define certain architecture goals that should help us make the right decisions. The ones most often seen by me are similar to:

  1. Scalability: Designing software architectures that can seamlessly scale to accommodate growing user loads and increased data volume over time. This involves considering factors like load balancing, distributed computing, and modular design.
  2. Reliability and Availability: Building systems that are highly reliable and available, minimizing downtime and ensuring that users can access the system whenever they need to. This may involve redundant systems, fault tolerance mechanisms, and effective error handling.
  3. Maintainability: Creating software architectures that are easy to understand, modify, and maintain over the long term. This involves using modular and well-documented code, adhering to coding standards, and implementing effective version control.

I am definitely not the best programmer in the world. That being said, I wish the systems were simple and readable. So are we able to bring out the essentials of our architecture in a simple way? I would answer yes:

  • Use interfaces instead of implementations
  • Keep your framework under control
  • Package by feature
  • Don’t be afraid to break conventions

Summary

This post is just a theoretical introduction to the issue of hexagonal architecture. I tried to convey that when making a decision that has long-term implications, we should analyze well the broad context regarding our system.
Writing code is simple. I personally believe that practically anyone can master this skill in about 3-6 months. It is definitely more difficult to work with code that was created several years back. So it is worthwhile to plan our solution well, but also not to be afraid of its evolution.
I invite you to read part two, in which I will attempt to implement hexagonal architecture in a simple and understandable way for everyone.


Leave a Reply

Your email address will not be published. Required fields are marked *