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, 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 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, 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, 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 its various user interfaces or databases in a remarkably similar way. Therefore, all of these are external entities that can be separated from the core app and made to communicate with it in a technology-agnostic way.
Sounds confusing? It will all be clear when we take the concept 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 hexagonal architecture follows a couple of basic principles:
- Explicit separation of user-side, server-side, and business logic.
- The separation is achieved through the use of Ports and Adapters.
- All dependencies move from the user-side 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 entities 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 – ports/adapters work with both the external actors 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. 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. The hexagon shape 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 – summary
And here we are! What do you think? Is hexagonal architecture something you could use in your project? 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 entities such as user interfaces and databases from the core application.
- It has a lot of benefits for the testability and scalability of your application.
- 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!