19 October 2021
Hexagonal architecture – is it for me? A no-nonsense overview
Developers never stop in their effort to make apps more efficient and scalable. One expression of this effort is the constant search for new architecture patterns. After all, the way you organize your app right from the start can make a world of difference in terms of scalability, maintainability, efficiency of implementing the business logic and more. Hexagonal architecture is one such pattern. Let’s learn more about it.
Have you ever thought about hexagonal architecture? In this article, we’re going to explain what it is, how it works and when your organization should go for it. We’re also going to make a simple exercise for those more core-oriented.
Let’s start with some background.
Software architecture trends
Hexagonal architecture can be better understood in the wider context of modern software architecture trends.
The concept of a traditional layered architecture is simple – by dividing your application into separate chunks, which include modules and classes that perform similar tasks, you gain a lot of benefits in various areas of software development. In particular, you get separation of concerns and domain logic, which makes tracking bugs, duplicates and other maintenance issues much easier.
There are many examples of layered architecture. The most common type involves division into Presentation Layer, Logic Layer / Domain Layer, and Data Layer.
Domain-driven design is yet another trend, which makes a case for layered architecture. On the architectural level, it promotes the benefits of organizing the structure of your application in accordance with the business functions it fulfills. Separating the business logic from the user interface or database definitely helps in achieving that.
With that in mind, let’s get to the hexagonal architecture.
Hexagonal architecture – the genesis
The concept of hexagonal architecture was first introduced in 2005 by an American computer scientist Alistair Cockburn, well known also for his contributions to the Agile movement.
Cockburn reached a conclusion that the core application interacts with the user interface or databases or automated test in a remarkably similar way. Therefore, all of these external systems can be separated from the core app / business logic and made to communicate with it in a technology-agnostic way.
Sounds confusing? It will all be clear when we take the concept of the hexagonal architecture under a microscope.
Hexagonal architecture – let’s break it down!
This is a basic overview of hexagonal architecture. In the following sections, we’re going to briefly talk about each high-level element of this architecture.
The basics – ports and adapters
The hexagonal architecture follows a couple of basic principles, including adapters architecture, ports and adapters:
- Explicit separation of user-side, server-side, and business logic.
- The separation is achieved through the use of Ports and Adapters (adapters architecture).
- All dependencies move from the user-side / user interface and server-side towards the business logic.
In hexagonal architecture, the core application includes all the business logic as well as the services responsible for various functionalities and use cases. The core receives from and sends commands/queries to external systems using Ports and Adapters.
The term “ports” simply refers to entry points to the application core. They contain (typically technology-neutral) interfaces that make it possible for external entities to obtain a set of rules for communicating with the core. Since the ports are essentially just gateways, another agent is necessary to actually make the communication happen. These are adapters.
The adapters actively initiate the communication between external entities and the core. Each port can serve many adapters. A common example of a controller could be a REST controller or any other API request handler.
Here is an extremely important thing about the adapters architecture – ports/adapters work with both the external systems that start the communication (driving side) and the ones that receive it (driven side). But the exact mechanism slightly differs.
The driving actors are those that start the interaction with the application by initiating a query. For example, it can be the mobile application interface or user interface code of a web app. The user input passed into the UI is taken by the adapter and sent to the core through the port. Both the port (interface) and the implementation of the interface will be inside the core/hexagon.
The driven actors are those that need the core application to interact with them. It could be databases or even other applications. In this case, the application calls the external (driven) entity. Then, the driven adapter implements the port for the core to use. This time, the implementation is within the driven adapter.
Why a hexagon?
The six ends, on their own, don’t really have any particular meaning as far as application code is concerned. So why a hexagon?
The hexagon shape (as in the above example) is simply a convenient way to depict that in this particular architecture:
- The core logic and services are inside,
- They communicate with various external actors (ends) using Ports and Adapters,
- Those can be divided into driving and driven actors, which again is easy to depict using a symmetrical shape.
Let’s see now see what hexagonal architecture can do for you.
Hexagonal architecture – benefits
Organizing your code in the manner prescribed by the hexagonal patterns has a lot of potential benefits:
- When done correctly, it makes it possible to isolate the application and business logic from external factors so that they can all be tested easily and separately.
- At the same, their dependencies can be easily mocked.
- Designing the user interfaces by their purpose rather than technology ensures that your application’s technology stack can freely grow over time.
- Helps implement the Domain-Driven Design by making sure that the domain logic does not leak out of the core.
- The ports and adapters are just as replaceable as all the external entities, further contributing to the scalability of the entire application.
- The advanced separation of concerns also makes the app easier to maintain, as changing the code in one place or adding new dependencies/ways to interact with the app, do not require significant code changes.
- Since one can test outside dependencies without any extra mocking tools, improving the overall testability of the application.
TSH’s Frontend Developer Robert Goik believes that hexagonal architecture has the potential to make the app much easier to control in the long run:
We love acronyms. And there are a couple of rules behind these catchy slogans that are worth following. Let’s try not to over-engineer and follow the YAGNI rule and the KISS rule. The key aspects of building quality, maintainable software are modularity, separation of concerns, and making an application loosely coupled.
The most important thing is to not end up with a big ball of mud, and it does not matter if we build a monolith or a microservices-based application. Hexagonal architecture goes a long way towards achieving it.
Hexagonal architecture – when to use?
With all those benefits, it would seem that using hexagonal architecture is a no-brainer? Is it really the case? Let’s ask Robert again:
“Should we use hexagonal architecture in our projects? As always the answer is “it depends”. If it is a fairly simple CRUD application, it is probably not worth it. However, the more complex the project is, the more sophisticated solutions are required.
We build applications with a bunch of different tools. Frameworks are one of them. Frameworks have the tendency to dominate. With the hexagonal architecture, we can keep frameworks in line and keep the Core Application framework agnostic.”
To sum it up – organizing your app in a way hexagonal architecture demands and keeping the separation of concern is not necessarily an easy task. It does require skill and time. But it will pay off in the long run. Providing that your app is big and complex enough for the “long run” to be worth it!
If it isn’t, you may consider implementing only some aspects of the architecture to improve separation of concern. There are many ways to go about it and it is something that you should discuss with your development team as the answer may be different for any given project.
Hexagonal architecture in practice
Let’s make a simple exercise to show how hexagonal architecture works in practice.
Only recently, a colleague at TSH published a very interesting read concerning TSH’s Node.js boilerplate. It just so happens that this is the perfect opportunity to verify if a boilerplate works with hexagonal architecture.
Let’s track a simple HTTP request that will create a new user and send an email notification.
- First, we send a HTTP POST request. To this end, we will use the express.js framework, which will invoke an action:
- The action will execute a command:
We need to have a delivery mechanism. In this case, the CQRS pattern (Command and Query Responsibility Segregation) is in use. The Command Bus will be our delivery mechanism that will execute CreateUserCommand. Commands are state-changing operations (we will create a new user in the database). Queries (executed by Query Bus respectively) are data-retrieval operations.
At this point, we can see that the command can be executed in any way. Cron Job, GraphQL mutation, or CLI interface. We are not coupled to express.js and HTTP POST requests.
- Finally, the Command bus will trigger the handler by executing the command:
Here we can see that the new user is created in the database with the use of userRepository. Then, EmailNotificationEvent is dispatched, which will trigger EmailNotificationService.
The email notification service is outside our core application and we communicated with the database through a repository.
With hexagonal architecture we should fairly easily replace the framework, it should be framework agnostic. To be compliant with hexagonal architecture we need interfaces that define the contract that we need to hold on. For example our userRepository will implement a repository interface, and should stick to it. The same goes for the EmailNotificationService. We should be able to use the service MailchimpService, SendGridService, or any other concrete implementation with our EmailNotificationService, defined by specific interface.
Hexagonal architecture with ports and adapters – summary
And here we are! That’s almost it for today’s blog post. What do you think? Is hexagonal architecture something you could use in your project? We sure think so. We’re also going to continue to expand this article to make it even better, with topics such as onion architecture, application core, REST API implementation, batch scripts, dependency inversion, application layer separation, domain objects, database access, domain services, http requests, user data, or dependency injection.
To sum up the most important things:
- Hexagonal architecture is a pattern that uses the mechanism of ports and adapters to achieve separation of concerns and isolate external systems and other external code such as user interfaces and databases from the core application.
- It has a lot of benefits for the testability and scalability of your application and in terms of freedom of actual implementation of your core domain, application layer and business logic.
- But it requires a certain level of complexity from the app for these benefits to truly shine.
- A partial implementation of the architecture may be a solution for smaller projects, but it requires further consultations with developers on a project-by-project basis.
It may be our team!
Contact us and tell us about your project.
We’ll tell you if hexagonal architecture or one of its variants is for you. We will also provide you with a lot of other useful advice so you can get the most out of it!