Software Engineering: A Modern Approach
1 What is a Hexagonal Architecture?
1.1 Introduction
The concept of Hexagonal Architecture was proposed by Alistair Cockburn in the mid-90s in an article posted on the first wiki ever created, called WikiWikiWeb (which primarily covered Software Engineering topics).
The goals of Hexagonal Architecture are similar to those of Clean Architecture, as we described in another article. Specifically, the idea is to build systems that have high cohesion, low coupling, and are easier to test.
A Hexagonal Architecture divides the classes of a system into two main groups:
Domain classes, which are directly related to the system’s business logic.
External classes, such as those related to the user interface and integration with external systems (such as databases).
Moreover, domain classes should not depend on classes related to infrastructure, user interface, or external systems. The main advantage of this division is to decouple these two types of classes.
As a result, the domain classes remain independent of the technologies–—including databases, user interfaces, and any other libraries–—used by the system. Consequently, technology changes can be made without impacting the domain classes. Perhaps even more importantly, domain classes can be shared across more than one technological platform. For example, a system may have various interfaces (Web, mobile, etc.). Furthermore, and equally important, it is easier to test the domain classes when they are decoupled from non-business-related classes.
In a Hexagonal Architecture, communication between the classes of these groups is mediated by adapters, which are classes that implement the same-name design pattern we studied in Chapter 6. We will further explain the role of the adapters shortly.
The architecture is usually represented by two concentric hexagons (see the following figure). The domain classes (or business classes, if you prefer) are in the inner hexagon. In the outer hexagon, we have the adapters. Finally, the databases and other external systems are located outside these two hexagons (and, thus, are not represented in the figure).
Cockburn justifies the use of a hexagon as follows:
Each face of the hexagon represents some
reasonthe application is trying to talk with the outside world. This is why it is concentric hexagons and not concentric circles.
Among the reasons that require communication with the outside world, we can mention the following: interacting with users (via a user interface, such as Web, mobile, console, etc.), persisting data, and sending data to other systems.
1.2 Ports and Adapters
In Hexagonal Architectures, the term port designates
the interfaces provided or required by the domain
classes (note that interface
here means a programming language
construct; for example, a Java interface).
There are two types of ports:
Provided Ports (or Provided Interfaces) are interfaces used for communication from the outside to the inside, that is, when an external system needs to call a method in the domain. These ports define the services (or the API) provided by the domain, representing the services that the domain implements for the outside world.
Required Ports (or Required Interfaces) are interfaces used when a domain class needs to call an external method. These ports declare the services from the outside world that are necessary for the domain to operate.
Finally, we have the components located in the outermost hexagon of the architecture—the adapters—that work in one of the following two ways:
Adapters can receive method calls coming from outside the system and route these calls to appropriate methods of the domain (as defined in provided interfaces). For example, these adapters can use REST, GraphQL, or gRPC to communicate with the external systems. Consequently, the domain remains independent and decoupled from such technologies.
Adapters can receive calls originating from the domain and direct them to an external system, such as a database or a credit card processing system. In particular, adapters that communicate with databases are usually called repositories.
1.3 Example: Library Management System
The following figure shows the hexagonal architecture of a library management system:
In the figure, we can see that users access the system using three interfaces: Web, mobile, and an external system. In all cases, this access is mediated by the adapters at the top of the figure. These adapters call methods defined in a port (or interface) provided by the domain. This interface might define methods for searching for books, making loans, and registering users in the library, among others. These methods are implemented by domain classes.
To fulfill its mission, the domain also needs to persist data. For this purpose, it can use the services defined in a required port (or interface), which defines methods for saving and retrieving book data, saving and retrieving loan data, among other operations. An adapter in the bottom part of the figure implements these methods by accessing a relational database. Consequently, this database is an external system located outside the two hexagons.
In Hexagonal Architectures, we can have several required and provided ports, all located in the inner hexagon, along with the domain classes. Moreover, we have two types of adapters: (1) adapters that call methods defined in ports (or interfaces) implemented by the domain; and (2) adapters that implement the methods of a port (or interface) required by the domain.
1.4 Conclusion
In 2005, Cockburn suggested that his architecture could also be called Ports and Adapters Architecture, providing the following justification:
I finally had this
aha!moment (June 2005) in which I saw that the facets of the hexagon areportsand the objects between the two hexagons areadapters[…] and so the architectural pattern is Ports and Adapters Architecture. It admits of a better explanation than what I could come up with in Hexagonal Architecture.
However, this alternative name did not gain widespread adoption, and the architecture continues to be better known as Hexagonal Architecture.
More Info
We recommend two short articles by Alistair Cockburn on WikiWikiWeb. The first article discusses the main characteristics of a hexagonal architecture. The second article explores the concepts of ports and adapters.
For further reading, you can also refer to our article on Clean Architecture.
Exercises
1. In a Hexagonal Architecture, an adapter is an implementation of the same-name design pattern. What about the ports? Can they be seen as an approximate implementation of which design pattern? If necessary, consult Chapter 6 to answer.
2. In the figure illustrating the hexagonal architecture of the library management system, why are the user interface adapters (HTTP, GraphQL, and REST) and the persistence adapters (SQL) on distinct faces of the hexagon? Could they be placed on the same face?
3. The use of the term hexagonal is arbitrary, as depending on the application, it can be called quadrangular, pentagonal, heptagonal, octagonal, etc. Justify this statement.
4. The following code shows two domain classes used in the documentation of Django, a well-known framework for building web applications in Python. The code defines rules for mapping fields from these classes to columns of database tables. Does this implementation of domain classes follow the principles of a hexagonal architecture? Justify your answer.
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
5. Compare and contrast the Hexagonal Architecture with the Clean Architecture, which we studied in a previous article.
Check out the other articles on our site.