Initial Impressions of Hexagonal Architecture

I recently started working in a codebase that applies the hexagonal architecture pattern, also known as ports and adapters. While this is a well established design pattern (it’s at the heart of Uncle Bob’s clean architecture) I nor most of the developers I know had much knowledge of it.

Hexagonal architecture was devised by Alistair Cockburn to address the usual issues that can arise in object-oriented software; testability and tight coupling. The pattern is described in detail on his website here. The key concept is defining a hard boundary around “the application” where core domain and business logic is inside that boundary and everything else is outside it, including UI and databases. The inside of the application exposes functionality through “ports” (often an interface) and the outside of the application implements the use of ports with “adapters”.

Rather than writing up yet another explanation of the pattern (there are plenty of good ones online already) I thought I’d give my initial impressions of using it and highlight what I’ve found to be important or interesting aspects.

If you’re not familiar with hexagonal architecture I recommend these links for a crash course (the first link should give enough of an introduction to follow the rest of this article):

Learning the Pattern

The core concepts of the hexagonal architecture are pretty straight-forward. You identify the application’s “inside” layer of domain and business logic and the “outside” layer of systems that will use or be used by it (I talk more about what “use or be used by” means in the Types of Ports section). Ports give access to the inside layer and adapters plug outside systems into ports. Pretty simple as high level concepts and the implemented code generally matches these concepts in structure, so following the pattern in the code should be simple.

Code Comprehension

I’m a big advocate of readable and comprehensible code and I find the hexagonal architecture naturally encourages this. Arranging the code along the lines of the pattern gives clear groupings that correlate to easily identifiable real world concepts. You can talk about the system using the pattern’s vocabulary and it maps clearly to the implementation in code, e.g. system X uses adapter Y to access port Z which loads data using port A with adapter B.

Testability

The hexagonal architecture lends itself beautifully to well structured tests. A test becomes an adapter for a primary port and test data is populated through secondary ports with mocks or stubs. You have a direct mapping between tests and application functionality.

Deeper Concepts and Details

Types of Ports

A key concept to the pattern is that there are two types of ports and adapters; primary and secondary (sometimes called inbound and outbound). I feel that much of the reading I’ve done on it fails to emphasise this concept; understanding this properly really made the whole design “click” for me.

Primary ports and adapters are often described as “coming in” to the application with secondary “going out” but the explanation that clarified this concept for me was that primary adapters use the application and secondary adapters are used by the application.

An example:
A web page uses a REST API (primary adapter) which calls a service class instance (primary port) to execute business logic (internal application).
The business logic (internal application) has a repository interface (secondary port) that it calls an implementation of (secondary adapter) to load the required data from a database.

Identifying Ports

The aspect of the pattern I found most open to interpretation and discussion was how to identify the primary ports of the application. Cockburn has thoughts on this:

“What exactly a port is and isn’t is largely a matter of taste. At the one extreme, every use case could be given its own port, producing hundreds of ports for many applications. Alternatively, one could imagine merging all primary ports and all secondary ports so there are only two ports, a left side and a right side. Neither extreme appears optimal.”

The codebase I’m currently working in does essentially have a port for every use case. It is certainly the simplest way to implement the system, but I agree with Alistair that it doesn’t seem optimal. One way of reducing the number of ports would be to aggregate use cases into groups that tend to change together, with a single port for each of these groups. The pros and cons of more versus less ports will generally come down to looser coupling versus implementation overhead. It is something that I’ll form a stronger opinion on as I get more experience using the pattern.

Recommendation

While I still consider myself inexperienced with the hexagonal architecture pattern, I’ve quickly become an advocate for it. I’ve found it to be intuitive to learn and implement and successful in providing the benefits that I want from an object oriented design pattern: comprehensible, decoupled and maintainable software. I’m surprised that it seems to be relatively obscure; almost none of the developers I know have worked with it and I don’t recall seeing it discussed almost ever in online forums. I highly recommend exploring it as a design pattern that should be simple and effective to learn and implement for any object oriented system.

Leave a comment