January 07, 2026

Hexagonal Architecture

Abstract

Hexagonal Architecture is a source code architecture pattern; it is a pattern for organizing your platform’s source code. Also known as Ports and Adapters Architecture, it is a software architectural pattern which separates an application’s core business logic (source code) from external systems or technologies such as databases, user interfaces, or third-party services. The goal of Hexagonal Architecture is to make the core business logic source code independent of any external systems, ensuring the source code remains flexible, maintainable, and testable. Alistair Cockburn documented the Hexagonal Architecture (ports and adapters) in a work published in 2005 (Wikipedia). Cockburn stated the intent of the architecture is:

“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.”

This post summarizes Hexagonal Architecture. It is a guide for those with previous knowledge of this architecture. The roles, responsibilities, and characteristics of each hexagonal layer are described. Finally, an example pattern for organizing your source code is presented.

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Overview

An overview of Hexagonal Architecture is shown in Figure 2.1.

Figure 2.1 – Hexagonal Architecture Overview (Artisan image)

Hexagonal Architecture Overview
Hexagonal Architecture Overview

Framework is the outermost layer and defines which drivers are supported. A driver is a means of communicating with one of the Frameworks. Common drivers are (a) HTTPS & web browser, (b) JMS & messaging, and (c) SFTP & data files. A Framework has one responsibility which is to adopt requests and responses between the driver and the Application.

Application is the middle layer and defines interactions with the Domain. The Application has two responsibilities.

  1. Coordinate Domain operations to fulfill whatever request came in through the Framework. For example, a single request may require the execution of multiple Domain operations, which the Application would coordinate.

  2. Implement Domain interactions with external resources. For example, the Domain may need purchase order data, which the Application is responsibility for getting on the Domain’s behalf. The Domain is indifferent to the implementation details of interactions with external resources, allowing the Application to update as needed; database one day, API the next.

Domain is the core layer and defines the business. The Domain has one responsibility which is to implement business features.

Further information about each hexagonal layer is provided next.

Domain

The Domain is the core hexagon. Its purpose is to implement all business processes and to abstract all external resources (data) needed to accomplish it. The Domain is free of any Framework details (HttpServletRequest, Session, RestTemplate, etc.) and Application details (JDBC, JMS, REST, etc.).

Figure 3.1 – Domain (Artisan image)

Domain
Domain

The Domain IS your application. The scope of the Domain code is the business requirements, and it implements these details. Anything other than this is abstracted by a Domain secondary port - an interface. This interface is later implemented in the Application. For example, to use purchase order data, the Domain defines an interface to abstract its retrieval. This interface is a Domain secondary port. This interface is implemented in the Application by an appropriate means like querying a database, reading a file, or calling an API.

The Domain receives no raw requests from drivers (HTTPS, SFTP, etc.). Domain primary ports are classes which define communication with the Domain without any driver details.

Domain Model

A model based on DDD object types implements the core business logic of the Domain. The following are common, but not an exhaustive list of Domain model objects.

  • Entity. An object that is not defined by its attributes, but rather by a thread of continuity and its identity (i.e. primary key) Example: Most airlines distinguish each seat uniquely on every flight. Each seat is an entity in this context. However, some airlines do not distinguish between every seat; all seats are the same. In this context, a seat is a value object.

  • Value object. An object that contains attributes but has NO identity (i.e. no primary key). They should be treated as immutable. Example: (1) S, M, L, XL, XXL, (2) Red, Green, Blue, and (3) Name. These are values that are part of an entity but are not uniquely identifiable by themselves (i.e. no primary key). A person’s name may be stored as a String or a custom Name inside an Entity. This Name object would be a value object.

  • Aggregate. A collection of objects that are bound together by a root entity, otherwise known as an aggregate root. An aggregate does NOT have its own unique identity (primary key) as it is a collection of separate entities. The aggregate root guarantees the consistency of changes (ACID) being made within the aggregate by defining a transactional boundary for all Entity objects it contains. Example: When you drive a car, you do not have to worry about moving the wheels forward, making the engine combust with spark and fuel, etc.; you are simply driving the car. In this context, the car is an aggregate of several other objects and serves as the aggregate root to all the other systems.

  • Factory. Methods for creating Domain objects should be delegated to a specialized Factory object such that alternative implementations may be easily interchanged. A Factory is for creating in-memory objects, not persistence.

  • Command. A Command is a user-initiated operation which may be rejected or accepted. If REJECTED, then command processing stops and nothing happens. If ACCEPTED, then command processing continues, and a Domain task/operation occurs. In either case (accepted or rejected) a Domain Event may be published.

  • Service. When an operation does not conceptually belong to any object. Following the natural contours of the problem, you can implement these operations in services.

  • Event. A Domain object that defines a Domain event; something that happened in the past. A Domain Event is an event that Domain experts care about.

  • Publisher. Methods for publishing Domain Events should be delegated to a specialized Publisher object such that alternative publication implementations may be easily interchanged. No implementation details are in the Domain. The Application is responsible for implementing (JMS, SFTP, SMTP, etc.) the publication.

  • Repository. Methods for retrieving Domain objects should be delegated to a specialized Repository object such that alternative storage implementations may be easily interchanged. No implementation details are in the Domain. The Application is responsible for implementing (SQL, data file, REST API, etc.) the repository to provide the data.

  • Sender. Methods for sending Domain objects should be delegated to a specialized Sender object such that alternative sending implementations may be easily interchanged. No implementation details are in the Domain. The Application is responsible for implementing (email, SMS) the sender.

Domain Primary Ports

A Domain primary port is an entry point into the Domain. An entry point is any inbound interaction with the Domain by the outer layers, almost always the Application. A Domain primary port has the following characteristics:

  • It is a concrete class.
  • It defines operations used by the Application to interact with the Domain.
  • It is implemented in the Domain by using:
    • Command objects
    • Service objects
  • It is injected into an Application primary adapter.

Figure 3.2.1 – Domain Primary Port UML

Domain Primary Port UML
Domain Primary Port UML

Domain Secondary Ports

A Domain secondary port is an exit point out of the Domain. An exit point is any outbound interaction with external systems or resources. A Domain secondary port has the following characteristics:

  • It is an interface.
  • It defines interactions with an external system or resource such as a database, message broker, file system, API, etc.
  • It is NOT implemented by the Domain.
  • It IS implemented by an Application secondary adapter.
    • There may be multiple implementations.
  • Different implementations should be easy to use.

Figure 3.3.1 – Domain Secondary Port UML

Domain Secondary Port UML
Domain Secondary Port UML

Application

The Application is the hexagon wrapping the Domain. It has two purposes.

  1. Instantiate instances of Domain primary port classes, injecting them as dependencies into Application primary adapters.
  2. Implement the Domain secondary port interfaces as Application secondary adapters. The Domain is indifferent to the implementation details of these interfaces by the Application (database, file, rest, etc.), so long as the implementations fulfill the interface contract.

The Application is free of any Framework details (HttpServletRequest, Session, RestTemplate, etc.).

Figure 4.1 – Application (Artisan image)

Application
Application

Application Primary Adapters

An Application primary adapter instantiates and uses one or more Domain primary port classes to orchestrate and perform Domain-related operations. An Application primary adapter has the following characteristics:

  • It is a class.
  • It is injected with instances of Domain primary ports (classes) which are the entry points into the Domain.
  • It orchestrates the Domain primary ports (classes) to carry out Domain-related operations.

Application Secondary Adapters

An Application secondary adapter implements the Domain secondary port interfaces. How the Application implements the interface is not important to the Domain, provided it adheres to the interface contract. An Application secondary adapter has the following characteristics:

  • It is a class.

  • Implements a Domain secondary port which are the exit points of the Domain. The Application can implement the interface as needed (database, messaging, rest API, file, messaging, etc.).

  • Injected wherever a Domain secondary port is needed.

Application Primary Ports

An Application primary port is like a Domain primary port. An Application primary port is an entry point into the Application. An entry point is any inbound interaction with the Application by the outer layers, typically a Framework. An Application primary port has the following characteristics:

  • It is a concrete class.
  • It is implemented in the Application by using:
    • Command objects
    • Service objects
  • Defines operations used by the Framework to interact with the Application.
  • Translates the Application “language” into the Domain “language”.
  • Injected into a Framework primary adapter.

Framework

The Framework is the hexagon wrapping the Application. Its purpose is to adapt raw requests from drivers (users/HTTP, messages/JMS, files/SFTP, etc) to the Application.

Multiple Frameworks may exist, and they will use the same Application and Domain. For example, a website user and a marketing company both want to see the same data. The website user’s driver will be a web browser which will interact with a Framework capable of returning HTML. The marketing company’s driver will be a REST API which will interact with a different Framework capable of returning JSON. Since both Frameworks use the same Application and Domain, both return the same data.

Figure 5.1 – Frameworks (Artisan image)

Frameworks
Frameworks

Framework Primary Adapters

A Framework primary adapter instantiates and uses one or more Application primary port classes to adapt raw requests from drivers to the “language” of the Application layer. A Framework primary adapter has the following characteristics:

  • It is a class.
  • Injected with instances of Application primary ports which are the entry points into the Application.
  • Translates the raw driver “language” into the Application “language”. Common drivers are (a) HTTPS & web browser, (b) JMS & messaging, and (c) SFTP & data files.

Driver

A Driver exists outside of the hexagon. A Driver communicates with a Framework using a specific protocol and the Framework listens to requests on that protocol.

For example, a Driver can be a JavaScript framework (Angular, React, Vue, etc.) communicating with a Framework by HTTPS. This Framework uses @Path("helloworld") and @GET or @POST (Building RESTful Web Services with Jakarta REST, n.d.) to listen for HTTPS requests. Once an HTTPS request is received by the Framework, it is translated to Application-defined objects and handed off to the Application handler for processing. Multiple Framework projects may exist to support Drivers using different protocols. Common drivers are (a) HTTPS & web browser, (b) JMS & messaging, and (c) SFTP & data files. The different Frameworks hand off processing to the same Application and Domain, allowing access to the same business features.

Figure 6.1 shows this with 4 different raw drivers each supported by their own Framework. All the Frameworks, however, share the Application and Domain.

Figure 6.1 – Drivers (Artisan image)

Drivers
Drivers

Example Source Code Organization

Suppose the ABC Sales Report business feature is needed.

NOTE A feature (aka “business feature”) implements one and only one business process which supports the platform’s underlying business goals.

First, create a new source code repository with the following name:

abc-sales-report

Next, follow the Hexagonal Architecture pattern and create sub-folders based on the hexagon layers. Physically these are file system folders created on the file system inside the repository. Logically these folders have different names depending on the technology you are using. If you are using Java and Maven, you may refer to these folders as modules of a project. If you are using C# and Visual Studio, you may refer to these folders as projects of a solution. For this example, I will refer to them as modules. The names of the modules both identify the hexagon layer the code is related to and declare the intent of the code. For example, abc-sales-report repository may have the following modules:

/abc-sales-report
    /api
    /jms
    /application
    /domain
    /ui

Here is a description of each module:

/api A Hexagonal Architecture Framework module containing REST endpoints. Provides a way the outside world may interact with the Domain. Responsible for translating HTTPS requests into Application objects for processing.

/jms A Hexagonal Architecture Framework module containing messaging Java (JMS) listeners. Provides a way the outside world may interact with the Domain. Responsible for translating Java JMS messages into Application objects for processing.

NOTE You may be asking, “Why not name the module ‘framework’ and put all framework layer code in one module?” Recall from 6.1, there may be many different raw drivers that want to communicate with your platform and each raw driver will use a different protocol for communication. Following Hexagonal Architecture, separate the raw driver protocol handlers into different modules so each module is only responsible for translating its raw driver “language” into the application layer “language”. This is done so that ultimately the Domain can handle processing the request. Each deployment will include the Application and Domain code.

/application A Hexagonal Architecture Application module containing Domain orchestration and Domain secondary port (exit points) interface implementations. These implementations provide a way the Domain may interact with the outside world.

/domain A Hexagonal Architecture Domain module containing the business feature code. This layer should have no knowledge of the Framework or Application code. It should be easily testable without any external runtime environments and easily reusable as Framework or Application layer code is changed.

/ui A UI raw driver. Think single-page application technology like Angular. Responsible for translating user input into HTTPS (typically) to interact with the Framework /api module for requests.

Everything for the abc-sales-report is contained in a single repository. This helps the code adhere to the characteristics of Feature-Oriented (modular) Architecture.

NOTE These characteristics are the same as microservices. However, the word “microservice” is meaningless and its use should be avoided.

These “microservice” characteristics (Ma, medium, 2018) match up with Hexagonal Architecture in the following ways:

  1. Single purpose. The name of the repository is abc-sales-report. The name defines its purpose. If the code is doing anything other than supporting this report, the platform developers got the code wrong.

  2. Loose coupling. The Application code which implements the Domain secondary port (exit points) keeps coupling loose. If not, the platform developers got the code wrong.

  3. High cohesion. If you need to make a change to the ABC Sales Report, it should be clear this repository is the only place you need to go. If not, the system platform developers got the code wrong.

References

Cockburn, A. (n.d.). Hexagonal architecture. Alistair Cockburn. https://alistair.cockburn.us/hexagonal-architecture/. Cockburn’s original post.

Hexagonal architecture. (n.d.). https://fideloper.com/hexagonal-architecture. Description of the hexagon and the responsibilities of each layer.

Ports-And-Adapters. (n.d.). https://www.dossier-andreas.net/software_architecture/ports_and_adapters.html. Definition of primary ports, secondary ports, primary adapters, and secondary adapters.

Jfokus. (2020, February 16). Cubes, hexagons, triangles, and more: Understanding Microservices by Chris Richardson [Video 21:26 - 26:34 (5 minutes)]. YouTube. https://www.youtube.com/watch?v=rMDjuXTQVkk. Chris Richardson’s JFocus 2020 Presentation on Microservices with includes a brief overview of hexagonal architecture.

Wikipedia contributors. (2025, February 18). Domain-driven design. Wikipedia. https://en.wikipedia.org/wiki/Domain-driven_design. Domain driven design (DDD) list of common model object names and responsibilities.

Karol.Kuc. (2020, May 20). Hexagonal Architecture by example - a hands-on introduction. blog.allegro.tech. https://blog.allegro.tech/2020/05/hexagonal-architecture-by-example.html. Practical code example demonstrating package structure, class naming conventions, and how interfaces and implementations get put into the different layers.

Dziadeusz. (n.d.). GitHub - dziadeusz/hexagonal-architecture-by-example. GitHub. https://github.com/dziadeusz/hexagonal-architecture-by-example. Practical code example demonstrating package structure, class naming conventions, and how interfaces and implementations get put into the different layers.

Ma, Xiao. (2018, October 17). Microservice Architecture at Mediumhttps://medium.engineering/microservice-architecture-at-medium-9c33805eb74f.

Building RESTful Web Services with Jakarta REST :: Jakarta EE Tutorial :: Jakarta EE Documentation. (n.d.). https://jakarta.ee/learn/docs/jakartaee-tutorial/current/websvcs/rest/rest.html

No comments:

Post a Comment