Hexagonal Architecture

An architectural pattern — also called Ports and Adapters — in which the application core (domain and use cases) is isolated from external systems through abstract interfaces (ports), with concrete implementations (adapters) plugged in at the boundary.

Introduced by Alistair Cockburn (coined 2005, published 2006) to allow an application to be driven equally by users, automated tests, and other programs, and to be developed and tested in isolation from any runtime devices and databases.

Problem

Applications whose business logic directly depends on HTTP frameworks, databases, or external APIs become hard to test without spinning up infrastructure, and hard to evolve when technology choices change. There is no clean way to run the application with a “test double” for the database or to add a new delivery mechanism (CLI, batch job) without touching core logic. The core is polluted with infrastructure concerns.

Solution

Draw a clear boundary between the inside (application core: domain model and application services) and the outside (infrastructure: UI, databases, message brokers, external APIs). Define abstract ports at the boundary — interfaces owned by the inside. Build concrete adapters on the outside that implement those ports. The core depends only on ports, never on adapters.

The hexagon shape is representational: the faces of the hexagon represent different ports — each can have one or more adapters attached. It emphasizes that there is no “top” or “bottom” of the application — any external actor can interact via a port.

Key Components / Structure

        [UI Adapter]  [Test Adapter]  [REST Adapter]
               ↓             ↓               ↓
          [Driving Port / Inbound Port]
                      ↓
              [Application Core]
              (Domain + Use Cases)
                      ↓
          [Driven Port / Outbound Port]
               ↓             ↓               ↓
         [DB Adapter]  [Email Adapter]  [Kafka Adapter]

Three-layer internal structure:

LayerContents
DomainCore business logic; insulated from all external implementation details
ApplicationMediator between domain and framework layers; defines ports
FrameworkContains implementation specifics (adapters) for external world interaction
TermMeaning
PortInterface defined by the application core — the contract for interaction
Driving / Inbound PortPort through which external actors drive the application (e.g., OrderService interface called by REST controllers)
Driven / Outbound PortPort the application uses to reach the outside world (e.g., OrderRepository, EmailSender)
Primary (Driving) AdapterImplements an inbound port; initiates requests. Examples: REST controllers, CLI, web views
Secondary (Driven) AdapterImplements an outbound port; responds to application requests. Examples: PostgresOrderRepository, Kafka publisher, email gateway
Application CoreDomain model + use cases — zero dependencies on any adapter

Practical Example

  • OrderService (inbound port) — interface defining what the application can do
  • OrderRepository (outbound port) — interface for data persistence
  • OrderRestController (primary adapter) — HTTP → OrderService calls
  • SqlOrderRepository (secondary adapter) — OrderRepository → database implementation

When to Use

  • Applications requiring high testability — the core can be tested with in-memory adapters, no infrastructure needed.
  • Systems expected to change delivery mechanisms (add REST alongside existing CLI; add Kafka alongside REST).
  • When a clean boundary between domain and infrastructure is a design goal.
  • As the structural backbone for Clean Architecture or Domain-Driven Design applications.
  • When the same use case logic must be exercised by multiple external actors (users, scheduled jobs, test harnesses).

Trade-offs

Pros:

  • Application core is fully testable without any real infrastructure.
  • Adapters can be swapped without touching business logic (e.g., replace PostgreSQL with DynamoDB — change only the secondary adapter).
  • Multiple adapters can implement the same port simultaneously (REST + gRPC + CLI all driving the same use cases).
  • Forces explicit, named interfaces at boundaries — improves clarity and maintainability.
  • Easy to maintain: new features added to individual layers without affecting others.

Cons / pitfalls:

  • More indirection and boilerplate than MVC Pattern or flat Layered Architecture for simple applications.
  • The port/adapter distinction requires discipline to maintain.
  • Risk of creating overly thin adapters that just delegate — adding complexity without value.

DDD Integration (Domain-Driven Hexagon)

Sairyss’s domain-driven-hexagon repository demonstrates the synthesis of Hexagonal Architecture and DDD:

  • The domain layer (innermost) contains Aggregates, Entities, Value Objects, and Domain Events — zero infrastructure dependencies.
  • Driving ports (inbound) are defined by domain needs, not delivery mechanism needs: CreateOrderUseCase is a port; the REST controller is an adapter that calls it.
  • Driven ports (outbound) are defined by what the domain requires: OrderRepository is a port; PostgresOrderRepository is an adapter.
  • Port types summary:
    • Driving (primary/inbound): HTTP controllers, CLI handlers, message consumers, test harnesses — all drive the application through inbound ports.
    • Driven (secondary/outbound): Database repositories, email senders, external API clients, message publishers — all implement outbound ports defined by the application core.

Testing benefit: The domain layer and application services can be fully unit-tested using in-memory adapter implementations for all driven ports — no database, no broker, no network required. Infrastructure adapters are tested independently with integration tests.

Relationship to Clean Architecture

Clean Architecture is a formalization and extension of Hexagonal Architecture:

HexagonalClean Architecture
”Inside” (application core)Entity and Use Case layers
”Outside” (infrastructure)Interface Adapters and Frameworks layers
PortsInterfaces defined at the Use Case/Interface Adapter boundary
AdaptersConcrete implementations in outer layers

Both enforce the same inward-pointing dependency rule.

Real-World Usage

  • Widely adopted across ecosystems — the pattern applies equally regardless of language or framework.
  • The pattern underlies many enterprise application frameworks’ recommended project structure.
  • Used when a team needs to write comprehensive unit tests for business logic without a live database.
  • Frameworks that support dependency injection naturally enable this pattern by allowing adapters to be swapped at configuration time.
  • Clean Architecture — formalizes Hexagonal’s inside/outside split into named concentric layers
  • Repository Pattern — a secondary (driven) adapter implementing an outbound port (data access)
  • Layered Architecture — contrasting style; Hexagonal Architecture replaces strict horizontal layers with a core-and-boundary model
  • Domain-Driven Design — Hexagonal Architecture is the preferred structural pattern for DDD implementations