Stackbook Logo
architecture-descriptionestablished · low operational burden

Hexagonal Architecture

Also known as: ports-and-adapters, hexagonal-pattern

Intent

Isolate application core logic from delivery mechanisms and infrastructure by communicating only through ports implemented by adapters.

Problem

Business logic becomes entangled with web frameworks, databases, and message brokers, so it can only be tested through them and changing a technology means rewriting rules. Teams struggle to run fast tests or swap infrastructure.

Forces

  • •Business rules should outlive framework and database choices
  • •Tests need to run without real infrastructure
  • •Multiple entry points (HTTP, CLI, queue) must share one core
  • •Extra indirection costs code and onboarding time

Solution

Overview

Hexagonal architecture, introduced by Alistair Cockburn, treats the application as a core surrounded by ports and adapters. The shape is not literally six-sided; it simply shows that many different actors can touch the core from any side. The core knows nothing about HTTP, SQL, or queues.

How it works

Inbound adapters translate external requests into calls on a driving port (a use case interface). The core executes domain logic and calls driven ports for anything external. Outbound adapters implement those ports.

 [REST adapter] --> (OrderService port)
                         |
                    [ Domain core ]
                         |
 (OrderRepository port) --> [Postgres adapter]
 (PaymentGateway port)  --> [Stripe adapter]
interface OrderRepository { Order find(OrderId id); void save(Order o); }
class PlaceOrder {  // use case in the core
  PlaceOrder(OrderRepository repo, PaymentGateway pay) {...}
}

In tests you pass an in-memory repository and a fake gateway. In production the composition root wires the real adapters.

Pitfalls

  • Defining ports from the database schema rather than from what the use case needs.
  • Letting framework annotations or ORM entities into the core, which silently breaks inversion.
  • Creating a port for everything, including stable utilities like date formatting.
  • Putting validation or pricing rules in controllers, since they feel convenient there.
  • Treating the pattern as folder layout only; the real rule is the direction of dependencies, which should be enforced with architecture tests.

Key takeaways

  • Ports belong to the core; adapters belong to the outside.
  • Dependencies point inward, always.
  • The biggest payoff is fast tests and replaceable infrastructure.
  • Skip it for simple CRUD where the core is nearly empty.

✓ When to Use

  • •Long-lived business systems where infrastructure will change
  • •Services needing multiple entry points over the same logic
  • •Teams that want fast, infrastructure-free unit tests

✗ When Not to Use

  • •Simple CRUD apps with little business logic
  • •Short-lived prototypes or scripts
  • •Teams unfamiliar with dependency inversion and no time to learn it

Pros

  • +Core logic testable with in-memory adapters
  • +Technology can be swapped at the edges
  • +Clear boundary between business and plumbing

Cons

  • −More interfaces and mapping code
  • −Overkill for thin services
  • −Port design mistakes leak infrastructure concepts into the core

Cost Profile

Infrastructure

None — structural pattern

Operational

Low — no runtime footprint

Cognitive

Medium — dependency inversion discipline

Failure Modes

  • Ports that mirror database tables, leaking persistence into the core

  • Adapters containing business rules that bypass the core

  • One adapter per port with no real variation, producing ceremony only

Real-World Examples

Alternatives

    Related Patterns

      Competency Domains

      requirements constraintseconomics evolution