Skip to content

Hexagonal Architecture

Hexagonal Architecture

Ports and Adapters Pattern

1. Overview: Hexagonal Architecture, which fully isolates business logic from external technology via ports and adapters

    flowchart LR
    A["Business logic coupled<br/>to UI/DB/external APIs —<br/>any tech change has system-wide impact"] --"Clarify boundaries<br/>with ports and adapters"--> B["Internal domain core<br/>connects to the outside only via ports"] --"Technology independence,<br/>easy testing"--> C["A system that can be<br/>swapped out in any direction"]

    style A fill:#FFEBEE,stroke:#D32F2F,color:#000
    style B fill:#E3F2FD,stroke:#1976D2,color:#000
    style C fill:#E8F5E9,stroke:#388E3C,color:#000
  

Definition: An architectural pattern proposed by Alistair Cockburn that places an application’s core business logic (the domain core) at the center of a hexagon, connecting to external systems (UI, DB, external APIs) through ports (interfaces) and adapters (implementations), so business logic never depends on external technology at all.

Characteristics: (Driving Side, left) The external world that drives the application (UI, tests, CLI) — inbound adapters. (Driven Side, right) The external world driven by the application (DB, messaging, external APIs) — outbound adapters. (Dependency inversion) A conceptual precursor to Clean Architecture, fully separating inside from outside via the Dependency Inversion Principle (DIP).


2. Hexagonal Architecture’s core structure

A. The internal business logic (domain core)

    flowchart TD
    subgraph HEX["Domain Core (inside the hexagon)"]
        direction TB
        DOM["Domain Model<br/>Entity / Value Object —<br/>core business rules and state"]
        SVC["Domain Service<br/>business logic spanning<br/>multiple entities"]
        UC["Application Service<br/>orchestrates use cases,<br/>manages transactions"]
        DOM --- SVC --- UC
    end

    subgraph PORT["Ports (interfaces)"]
        direction LR
        IP["Inbound Port<br/>Use Case Interface —<br/>the entry contract for<br/>the outside to call the core"]
        OP["Outbound Port<br/>Repository/Gateway Interface —<br/>the exit contract for<br/>the core to call the outside"]
    end

    HEX --> PORT

    style HEX fill:#1E3A5F,stroke:#1E3A5F,color:#fff
    style IP  fill:#E3F2FD,stroke:#1976D2,color:#000
    style OP  fill:#E8F5E9,stroke:#388E3C,color:#000
  

Domain core components

ElementRoleAllowed dependencies
Domain ModelRepresents core business concepts, rules, and invariantsNone (the purest area)
Domain ServiceExecutes domain logic that doesn’t belong to a single entityDomain model only
Application ServiceOrchestrates use-case flow, calls out via portsDomain model, domain services, ports (interfaces)
Inbound PortInterface contract for the outside to call the coreNone (defined by the core)
Outbound PortInterface contract for the core to use external resourcesNone (defined by the core)

B. How ports and adapters connect

    flowchart LR
    subgraph DRIVING["Driving Side (inbound)"]
        direction TB
        A1["REST Controller<br/>handles HTTP requests"]
        A2["CLI Adapter<br/>command-line interface"]
        A3["Test Adapter<br/>unit/integration tests"]
    end

    subgraph CORE["Domain Core"]
        direction TB
        IP2["Inbound Port<br/>(Use Case Interface)"]
        DOM2["Business logic"]
        OP2["Outbound Port<br/>(Repository Interface)"]
        IP2 --> DOM2 --> OP2
    end

    subgraph DRIVEN["Driven Side (outbound)"]
        direction TB
        B1["JPA Adapter<br/>DB persistence"]
        B2["Kafka Adapter<br/>publish/subscribe messaging"]
        B3["HTTP Client<br/>calls external APIs"]
    end

    DRIVING -->|"Calls port"| CORE
    CORE -->|"Port implementation"| DRIVEN

    style DRIVING fill:#E3F2FD,stroke:#1976D2,color:#1E3A5F
    style CORE    fill:#1E3A5F,stroke:#1E3A5F,color:#fff
    style DRIVEN  fill:#E8F5E9,stroke:#388E3C,color:#1B5E20
  
CategoryPortAdapter
DefinitionInterface contract defined by the coreExternal technology code implementing the port interface
LocationInside the domain coreOutside the domain core (infrastructure layer)
Inbound exampleCreateOrderUseCase interfaceOrderRestController, OrderCLIAdapter
Outbound exampleOrderRepository interfaceJpaOrderRepositoryAdapter, InMemoryOrderRepository
How to swapKeep the port (interface) unchangedSwap only the adapter (implementation) — core code untouched

Layered Architecture vs. Hexagonal Architecture

ComparisonLayered ArchitectureHexagonal Architecture
Dependency directionOne-way, top to bottomAll dependencies point inward toward the core
Swapping externalsRequires modifying an entire lower layerSwap only the adapter
TestingHard to test without a DB or external APITest the core in isolation with in-memory adapters
ComplexitySimple and intuitiveSome upfront cost in designing ports/adapters

3. Expected benefits and practical application of Hexagonal Architecture

CategoryKey expected benefitPractical application
Technology independenceBusiness logic stays unchanged when swapping DB, UI, or frameworkSwap only the JPA adapter when migrating MySQL → MongoDB
TestabilityTest the domain with in-memory adapters and no external dependenciesUnit-test business logic without a DB using an InMemoryRepository
DDD alignmentBounded Context boundaries align with hexagonal core boundariesApply an independent hexagon per MSA service for autonomy
Multi-channel supportConnect REST, GraphQL, CLI, and messaging to the same core simultaneouslyServe Web, Mobile, and Batch simultaneously from one business logic core