Hexagonal Architecture

A way to isolate business logic in code

Kong To
14 min readMar 30, 2020

Table of contents

  • History
  • Purpose
  • Principles
  • The separation of concerns
  • Three Layers
  • Inner hexagon isolation — dealing with dependencies
  • Ports and Adapters
  • Domain in isolation
  • Direction of flow
  • Ease of testing
  • Sample with Spring and Kotlin
  • Hexagonal vs Onion vs Clean
  • Conclusion
  • Reference

A bit of history

Published in a blog by Alistair Cockburn in 2005

The hexagonal architecture is synonym to “ports and adapters pattern”, but nowadays “hexagonal architecture” is better known. Moreover, it is a step further than the four (4) layered architecture that allows to separate the layers per responsibility. The hexagonal architecture on the other hand clearly suggests a focus on the domain isolation.

The term “hexagonal” comes from the graphical conventions that shows the application component like a hexagonal cell. The six borders represents the multiple interfaces that the component may expose to the external world.

Hexagonal architecture, domain centric layered architecture

Purpose

The hexagonal architecture aims to :

“Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases.”

In other words, it aims to isolate the business logic from the rest within the application, codebase wise, so we can focus test automation on its behaviour independently. Thus it leads to divide a system into several loosely-coupled interchangeable components, such as the business logic, the database, the user interface, test scripts and interfaces with other systems.

This approach is more likely an extension instead of an alternative to the layered architecture.

Principles

There are three principles which we must rely on :

  • separate clearly the Application Programming Interface (API), domain and Service Provider Interface (SPI)
  • manage dependencies from outside in, toward the domain
  • isolate the domain from the outside concerns by using ports and adapters, via interfaces

Each component is connected to the others through a number of exposed “ports”. Adapters are the glue between components and with the outside world. Concretely adapters are the implementation of the ports.

“API”and “SPI” are my personal preference in terms of wording.

The separation of concerns

Hexagonal architecture is a pattern for designing software applications. It is aims to separate two concerns : domain (inner hexagon) and infrastructure (outer hexagon), delimited by the borders from the outside. The domain must not depend on the infrastructure. The inner sides of the hexagon are simply the representation of ports. And the outer sides are the adapters. The hexagon is balanced with some external services on the left (for other services to consume) and others on the right (service providers that our application consume). The execution flows from left to right.

Inner and outer hexagons

Three Layers

Based the first principle above, we can separate explicitly three main layers :

  • API (application programming interface), the exposition side

This layer is the client facing, on the left hand side. It is part of the infrastructure, some may refer it to exposition, which I like very much, but prefer using api (application programming interface). It allows other applications of the outside world to interact with this current application, by exposing APIs (HTTP, REST, Web-socket etc.). The external application can be a Single Page Applications (SPA), a programme or a middleware. This side is the driving side. because it drives the domain which in turn will apply business rules. It can also be seen as part of the infrastructure.

  • Domain

This layer is the one we want to isolate from the rest, because it hosts all the business logic. Referring to the Domain-Drive-Design approach, we rely on the ubiquity language, so we would use the domain specific vocabulary.

  • SPI (service provider interface), infrastructure client side

Almost every application needs to interact with tools and/or other systems, specifically known as a service provider interface (SPI). This is the driven side and is the second part of the infrastructure. And it is generally referred as infrastructure. In this layer, we want to allow the application to retrieve or send data to the outside world. The nature of interaction could be any kind, and typical ones are HTTP, File System, SMTP, SQL.

Three layers : API, Domain, SPI

In the diagram above, we can see three layers (API, domain and SPI). When structuring the code of a project, we usually would separate them clearly.

From here on, we will use an example based on a web service that exposes a resource as a REST endpoint, which will call the Service in the domain, which in turn uses the Repository to persist the data into a database. We will see the code samples later on in the sample section.

Inner Hexagon Isolation — Dealing with dependencies

When implementing this architecture, what matter most is the isolation the domain (code of services and domain) from the rest.

  • The outer hexagon has the web service adapter that uses the service logic in the inner hexagon, via port
  • The service in the inner hexagon uses the repository adapter (SPI) via port in the outer hexagon
  • The repository adapter (SPI) in the infrastructure (outer hexagon) does whatever is required to interact with the outside
Dealing with dependencies

The diagram shows one direction of dependency flow, from the left to the right. On the left hand side in, the api (exposition side) depends on the business logic in the domain, that is not a problem. However, on the right hand side, the business logic depends on the infrastructure (outer hexagon), and that is a problem we want to solve.

In terms of code, we would normally have the domain service making direct call to the code of the repository to persist data. In terms of dependency, that means the domain service depends on the repository implementation. The problem is that we would have coupling between the domain service and the repository logic. That is exactly what we want to avoid. Let’s see how we can inverse this dependency.

Domain Layer Isolation using Dependency Inversion

To be able to isolate the domain code from the outer hexagon properly, we need to change the flow of dependency. Using the dependency inversion principle, we can control dependencies from the outer hexagon (API, SPI), so that the domain does not depend on anything from the outside.

Dependency Inversion
  • The outer hexagon has the service adapter that uses the business logic in the domain layer

The api’s adapter depends on the service logic, which is not a problem. We can introduce an interface, so we can inject the service logic which implements the service interface. The interface allows to hide details of the business logic, thus the api’s adapter won’t see implementation of the service inside the domain.

But to keep it simple, we could also not inverting the dependency so we can avoid introduce a not so useful interface, because the workflow and dependency has the same direction.

  • The service in the inner hexagon uses the repository in the infrastructure (outer hexagon)

We don’t want the service logic to depend on the infrastructure, hence the repository implementation. To inverse that dependency, we introduce purposely an interface, called port, to abstract the its detail, so we can inject the repository adapter which implements the repository interface. The interface also allows to hide details of the business logic. The injection is done at the api level.

  • The repository adapter does whatever is required to interact with the outside

Ports and Adapters

Remember that the hexagonal architecture is a pattern for how to structure certain aspects of the application. It’s specifically about dealing with I/O.

  • The I/O is dealt at the outer hexagon
  • The sides of the inner hexagons are the ports.
  • The adapter on the outer hexagon (API and SPI) is plugged to the port that is in the inner hexagon
  • Finally, the centre is the business logic composed of services and domain.
Ports & Adaptors

The domain in isolation

I think it’s worth it to have a few words on this part. The hexagonal architecture is a domain centric style, emerged after the publication in 2003 of the red book, Domain-Driven Design: Tackling Complexity in the Heart of Software, by Eric Evans.

The architecture style allows us to “rigorously separate the various concerns of our application or system into well-defined layers”, red book page 119, by Vaughn Vernon.

To isolate the domain tightly, we need to apply the dependency inversion, and use a few design patterns to model our domain. Hence, if we define interfaces at the boundaries, left and right, we would not need to think about technical details on how a call are made (soap, json, http, rpc) and how data is stored (sql, nosql, key-value, filesystem).

If we don’t care about technical details, we can switch easily the framework or libraries that spawn the application or interact with the infrastructure, because our domain code does not depend on their.

There are a five (5) benefits of big matter :

  • no dependency on technical frameworks or libraries
  • no dependency on infrastructure
  • ease of testing, with focus on domain behaviour
  • readability and maintenance, clean and focus on the domain, technical details are secondary.
  • may simply begin to model the domain by omitting technical details

Direction of flow

Direction of uses vs direction of dependencies

The direction of call is from left to right, as shown on all schema. The other way around is forbidden.

On the other hand, the dependencies are from the outer (infrastructure) to inner (domain) layers, not the other way around.

When there is no domain logic, we may skip the domain layer, allowing the direct calls from driver to driven.

Ease of Testing

The hexagonal architecture provides some benefits. The most important one is testing automation. The application layer can completely be wrapped within a testing framework. The infrastructure can be mocked. This does not mean we don’t have to test the application layer code and the infrastructure code.

Domain testing in isolation to have good functional coverage, with several levels of tests

When it comes to testing, we may have a battery of tests to cover all levels of the testing pyramid. Depending on the context, we may need to have more or less extensive testing at different levels. For the purpose in this article, let’s just focus on the domain behaviour testing. The diagram above show two level of tests :

  • Unit Function Testing (UFT)
  • Component Testing (CT)

Unit Function Testing

Here, we also need to mock the infrastructure for the same reason. However, we can also simplify things by not bothering about the enveloping application, which most of the time uses some sort of framework. So here we are writing purely functional tests, and we can aim for 100% of coverage — at least close to 100% as the business behaviour matter.

Component Testing

The component testing implies to reach the endpoint, while mocking the infrastructure, for instance the repository to leverage the complexity of the connecting to a real database, within the testing application context. As opposed to the domain testing, tests on the infrastructure are technical. We aim for technical requirement : reliability, robustness, scalability. The serve a different purpose from functional tests, so we don’t want to get 100% of coverage, but rather we want to achieve technical objectives.

For the example we are using in this article, SpringBoot 5 is the framework that is exposing REST endpoints, powering up the domain code, and interacting with a database. The domain code is in pure kotlin. Think-fully, we want to focus only on the business behaviour.

By having the two levels of tests above, it does not mean we can exclude unit testing, as it serves other purposes, in addition to verify the functionalities.

Sample with Spring and kotlin

The project codebase is separated in three modules as cited in the “Three Modules” section :

  • API (referring to exposition)
  • Domain
  • SPI (referring to infrastructure)

The exposition module is using SpringBoot to power up the application, so it means that we are using the Spring framework. So far as we are concerned, the Spring IoC helps to manage the Dependency Injection. Beans, in the Java world, are declared using annotations prefixed with “@”. Typically @Service, @Repositiory, and @RepositoryRestResource the three main annotation we are interested in. Other annotations are used basically to configure the Spring context, or to declare the endpoints or entities.

One thing very important to notice is that we don’t use anything that is related to the Spring framework in the domain module.

Project structure

├── build.gradle.kts
├── domain
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ └── src
│ ├── main
│ │ └── kotlin
│ │ └── io
│ │ └── github
│ │ └── newlight77
│ │ └── bootstrap
│ │ └── note
│ │ ├── INoteRepository.kt
│ │ ├── INoteService.kt
│ │ ├── NoteDomain.kt
│ │ └── NoteService.kt
│ └── test
│ └── kotlin
├── api
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ └── src
│ ├── main
│ │ ├── kotlin
│ │ │ └── io
│ │ │ └── github
│ │ │ └── newlight77
│ │ │ └── bootstrap
│ │ │ ├── CorsConfiguration.kt
│ │ │ ├── DemoApplication.kt
│ │ │ ├── MvcConfiguration.kt
│ │ │ ├── SecurityConfiguration.kt
│ │ │ └── note
│ │ │ ├── NoteApi.kt
│ │ │ ├── NoteApiHandler.kt
│ │ │ ├── NoteConfiguration.kt
│ │ │ ├── NoteModel.kt
│ │ │ ├── NoteMvc.kt
│ │ │ ├── NoteServiceAdapter.kt
│ │ │ └── NotesDataInitializer.kt
│ │ └── resources
│ │ ├── application.properties
│ │ └── templates
│ │ ├── hello.html
│ │ ├── home.html
│ │ ├── login.html
│ │ └── notes.html
│ └── test
│ └── kotlin
│ └── io
│ └── github
│ └── newlight77
│ └── bootstrap
│ └── DemoApplicationTests.kt
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── spi
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ └── src
│ ├── main
│ │ └── kotlin
│ │ └── io
│ │ └── github
│ │ └── newlight77
│ │ └── bootstrap
│ │ └── note
│ │ ├── NoteEntity.kt
│ │ ├── NoteEventHandler.kt
│ │ ├── NotesJpaRepository.kt
│ │ └── NotesRepositoryAdapter.kt
│ └── test
│ └── kotlin
└── settings.gradle.kts

Code Snippets

You may notice that in this example, there is real business logic. Normally, the http call can be handled and do a passthrough to interact directly with the infrastructure, skipping the domain. So, when there is usefulness, let’s just skip the domain, as it’s purpose to hold business logic.

As a complement to that, business logic could either business rules or business processes, or both. We could then introduce submodules : a use case and domain model. So we would have application and domain inside a core module.

Here I keep it simple, by having just a domain module.

API layer (exposition as driver side)
Application layer
SPI layer (infrastructure driven side)

Hexagonal vs Onion vs Clean

We need to compare the three architecture base on four aspects :

  • Loose coupling from the external

All domain code is framework/library independent.

  • Dependencies direction

Only the clean architecture is explicit in pointing out that the dependencies direction is towards the centre, but hexagonal and onion need to apply to the Dependency Inversion Principle.

  • Layers

Hexagonal Architecture mentions two layers : inner and outer hexagon of the Application. Onion has four layers : Interfaces, Application services, Domain services and Domain model. Clean Architecture is similar to Onion : Interfaces, Application Services, Domain Service (use cases), Entities (Domain model)

  • Testability in isolation

In all three architecture styles, we can simply mock the external tools and delivery mechanisms and test the application code in insulation, without using any DB nor HTTP requests.

Onion Architecture

The onion architecture proposed by Jeffrey Palermo in 2008.

Onion architecture is similar to the hexagonal architecture. It also externalises the infrastructure with proper interfaces to ensure loose coupling between the application and the external. It decomposes further the domain into several concentric rings using dependency inversion.

Onion Architecture

Clean Architecture

The clean architecture proposed by Robert C. Martin in 2012.

Clean architecture combines the principles of the hexagonal architecture, the onion architecture and several other variants. It provides additional levels of detail of the component, which are presented as concentric rings. It isolates adapters and interfaces (user interface, databases, external systems) in the outer rings of the architecture and leaves the inner rings for use cases and entities. [It uses the principle of dependency inversion with the strict rule that dependencies shall only exist between an outer ring to an inner ring and never the contrary.]

Clean Architecture

Wrap up

The goal is to structure the code so that changes are easy, safe and fast to make. With a structure of code that helps to isolate the domain logic, it is lot easier to understand because we can sort out the business logic, while not being bothered by the technical code. As a consequence, the testing activities are quite simpler, because we first only write tests to secure the business logic, and then add some integration tests to cover the technical envelop. As testing serves as a safety net, making any change to the business logic, and even any technical code change, would be rather be less complex. Of course, along with automation, we can make changes fast, safely. And who knows, one day, we may also just change the framework that is enveloping the domain logic code without changing anything at all to domain code.

Hence, the architecture of code really matters as we can take a lot of benefits.

Let’s just have a look to visually see the difference between the three models of architecture discussed earlier, hexagonal | onion | clean. Finally we can notice that they are not so different. They all have the same goal, which is exactly to isolate the domain logic. I personally prefer talking about hexagonal architecture as it rings the bell easier, the concept and vocabularies are also simpler for further discussion. Of course, if things gets more complex, I’d just introduce more notion and vocabularies of the clean architecture.

Hexagonal | Onion | Clean Architecture

Acknowledgement

Thanks to Dan MAGIER for reviewing and helping me to clarifying some ideas in this article.

Reference

Glossary

  • API : Application programming interface
  • SPI : Service provider interface
  • Port : interface defined as contract interface in the domain for infrastructure to comply with when interacting with the domain
  • Adapter : Concrete implementation on the port on the infrastructure side
  • DIP : Dependency Inversion principle
  • CT : Component Testing
  • TDD : Test-Driven-Development
  • BDD : Behaviour-Driven-Development
  • UI/UX : User Interface / User Experience

Terminology

Write Medium in Markdown? Try Markdium!

--

--

Kong To
Kong To

Written by Kong To

Architect, Engineer, Developer, Code, Code Crafter... whatever but code quality matters

Responses (2)