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 that was 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. However, to remember, the idea is to build systems that have high cohesion, low coupling, and that are easier to test.
A Hexagonal Architecture divides the classes of a system into two groups:
Domain classes, that is, classes directly related to the system’s business.
Other classes, such as those related to infrastructure, 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.
In this way, the domain classes do not know 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 technology. For example, a system may have various interfaces (Web, mobile, etc.). Lastly, but also very important, it is easier to test the domain classes when they are decoupled from other non-business-related classes.
In a Hexagonal Architecture, communication between the classes of these groups is mediated by adapters, that is, by classes that implement the same-name design pattern we studied in Chapter 6. We will explain better the role of the adapters shortly.
The architecture is usually represented by two concentric hexagons (see the next figure). The domain classes (or business classes, if you prefer) are in the inner hexagon. In the outer hexagon, we have the adapters. Lastly, the databases and other external systems are located outside these two hexagons (and, thus, they 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 the users (via some interface, whether it is Web, mobile, console, etc.), persisting data, sending data to other systems, etc.
1.2 Ports and Adapters
In Hexagonal Architectures, the term port designates the interfaces provided or required by the domain classes (note that the interface here means a programming language interface; 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. Thus, these ports define the services (or the API) provided by the domain, that is, the services that the domain implements to the outside world.
Required Ports (or Required Interfaces) are interfaces used when a domain class needs to call am external method. Thus, these ports declare the services from the outside world that are necessary for the domain operation.
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, the adapters can use REST, GraphQL, or gRPC, to communicate with the external systems. Consequently, the domain becomes independent and decoupled of such technologies.
Adapters can receive calls originated from the domain and directed to an external system, such as a database or a credit card processor system. Particularly, adapters that communicate with databases are usually called repositories.
1.3 Example: Library Management System
The next figure shows the hexagonal architecture of a library management system:
In the figure, we observe that users can access the system using three interfaces: Web, mobile, and through an external system. In any case, 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. For example, this interface might define methods for searching for books, making loans, registering users in the library, etc. These methods are implemented by domain classes.
To fulfill its mission, the domain also needs to persist some data. For that, 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, etc. An adapter in the bottom part of our figure implements these methods by accessing a relational database. Thus, this database is an external system located outside the two hexagons.
In Hexagonal Architectures, we can have several required and provided ports, which are 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; (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, with 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 wasn’t so successful and the architecture, probably, continues to be more 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.
You can also check out our article about Clean Architecture.
Exercises
1. In a Hexagonal Architecture, an adapter is an implementation of the same-name design pattern. And the ports? Can they be seen as being an implementation—at least approximate—of which design pattern? If necessary, consult the Chapter 6 to answer.
2. In the figure with 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 definition of the term hexagonal is arbitrary, as depending on the application, it can be called quadrangular, pentagonal, heptagonal, octagonal, etc. Justify this statement.
4. Next, we show the code of two domain classes that are 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 the implementation of these 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. Describe the differences between Hexagonal and Clean Architecture (which we studied in another article).