Hexagonal Architecture (Ports and Adapters)
The application core defines ports; adapters connect ports to the outside world. Alistair Cockburn, 2005. The architectural shape that makes tests trivial and infrastructure swaps cheap.
The application core defines what it needs (ports). Adapters plug into those ports from the outside. Tests use fake adapters; production uses real ones. Status: STUB — promoted to OUTLINE in Y1 Phase 5.
What this pattern is
Hexagonal Architecture (also known as Ports and Adapters), introduced by Alistair Cockburn in 2005, asks the application core to define its needs as ports — interfaces describing the operations the core wants to perform on the outside world (or wants the outside world to perform on it). Concrete adapters then plug into those ports: a Postgres adapter implements the UserRepository port; an in-memory adapter implements the same port for tests; a Redis adapter for caching; a Kafka adapter for events. The core depends only on ports, never on adapters.
The shape is identical to Clean Architecture’s dependency rule; the framing differs. Hexagonal emphasizes the symmetry between driving adapters (HTTP, CLI, gRPC, message consumers — they call into the core) and driven adapters (databases, queues, third-party APIs — the core calls into them). Both sides are interchangeable. Cockburn’s original diagram was hexagonal specifically to break the top-down bias of layered architectures. The hexagon has no top or bottom, no natural “outside” — every adapter is equally connectable.
The mental model that makes the pattern click: imagine your application as a hexagonal-shaped device with plug sockets on each face. Some sockets are for input (HTTP requests, CLI commands, message queue messages). Some are for output (database calls, third-party APIs, filesystem operations). The device doesn’t care what plugs into each socket; it only knows the shape of each socket. In development, a mock socket-plug provides fake behavior. In production, real socket-plugs connect to real infrastructure. Same device, different plugs.
Concrete instances in the wild
- Spring Boot services with interface-driven design. Interfaces defined in domain packages,
@Serviceimplementations in infrastructure packages. Spring wires them at startup. - Go services with mock interfaces. Go’s structural typing makes ports and adapters natural. Define an interface for what you need; write both the real implementation and a test double.
- Node/TypeScript with DI containers. InversifyJS or NestJS
@Injectablewith abstract classes as ports. Adapters are concrete class implementations. - Testable game engines. Game logic (the core) depends on a
RenderPort,InputPort,AudioPort. Different adapters plug in for different platforms (desktop, mobile, web). The core code is platform-agnostic. - Payment processors. A
PaymentGatewayport with Stripe, Braintree, PayPal, and test adapters. Swapping payment providers is an adapter change, not a domain rewrite. - CQRS with distinct read and write ports. Command handlers speak to write-side ports (transactional database); query handlers speak to read-side ports (denormalized read models). Both are hexagonal adapters.
- Legacy modernization. Wrapping a legacy system as an adapter that implements a modern port lets the domain code use a clean interface while the adapter handles the ugly. Strangler-fig migration relies on this shape.
- Microservices with clean boundaries. Each service’s public API is a driving adapter (HTTP handler) over its domain. Each of its outbound calls is a driven adapter (HTTP client or message publisher). Every service is hexagonal.
Why this pattern matters
The pattern’s payoff is where testability actually happens. Domain code that depends only on ports (interfaces) can be tested with in-memory adapters. Tests run in milliseconds because there’s no I/O. The test suite for a well-factored service can execute the entire domain layer in under a second. That’s the difference between a codebase you can refactor with confidence and one you can’t.
The pattern also makes infrastructure swaps cheap. When Postgres becomes CockroachDB, only the UserRepositoryImpl adapter changes. When REST becomes gRPC, only the driving adapter (HTTP handler → gRPC service) changes. The domain code, which is where the business value lives, doesn’t move. This is what “code you write once and keep for a decade” looks like structurally.
There’s a subtler benefit: hexagonal architecture makes reviewing pull requests faster. A reviewer opens a diff and can immediately tell “this is a domain change” or “this is an adapter change.” Domain changes need business-logic review; adapter changes need infrastructure review. The two concerns don’t tangle in the same file, so review focus is easier.
Depth progression
STUB ← you are here.
OUTLINE Promoted when Y1 Phase 5 walks ports and adapters with a concrete example.
DEEP Promoted when a Y2 service is structured this way AND you've swapped at
least one adapter (real DB for in-memory in tests; real Kafka for in-memory
queue in CI) without touching the core.
Preview: what OUTLINE will answer
When Y1 Phase 5 promotes this entry to OUTLINE, it will name:
- PROBLEM. How do you build applications that are testable, infrastructure-agnostic, and cheap to evolve as tools change?
- PRINCIPLES. Application core defines interfaces (ports) for its needs. Adapters implement those interfaces. Driving adapters call into the core; driven adapters are called by the core. Symmetry between input and output boundaries. Core doesn’t know which adapter is plugged in at any moment.
- TRADE-OFFS. Interface count and granularity (too few = leaky, too many = ceremony). Static wiring (constructor injection) vs dynamic (service locator, container). Language support for interfaces (Go, Rust, TypeScript) vs languages where it’s cumbersome (raw JavaScript, older Python).
- TOOLS (time-stamped as of 2026-06): Cockburn’s original 2005 article, Spring Framework’s
@Autowiredwith interfaces, Go’s structural interfaces, TypeScript’sinterfacekeyword with DI containers, Alistair Cockburn’s later refinements at alistaircockburn.com.
The DEEP promotion, after Y2 with real adapter swaps, will add MASTERY (operating a hexagonal service through infrastructure changes), COMPARE (hexagonal vs pure clean architecture — near-identical, framed differently), OPERATE (adapter-related incidents like leaky abstractions where the adapter shape didn’t match the domain need), and CONTRIBUTE (a refactor that introduced a port where one didn’t exist before, documented as an ADR).
Canonical references
- Alistair Cockburn, Hexagonal Architecture article (2005) — the original short paper. Free at alistaircockburn.com.
- Alistair Cockburn’s later writing at alistaircockburn.com — updated thinking on ports and adapters.
- Vaughn Vernon, Implementing Domain-Driven Design — chapter on hexagonal architecture as it composes with DDD.
- Herberto Graça’s comparison of Hexagonal, Clean, Onion, and Ports and Adapters at herbertograca.com.
- Chris Fidao’s Servers for Hackers, ports and adapters series — practical Laravel implementation.
Cross-references
- Y1 Phase 5: Software Architecture Patterns
- Related: clean-architecture, repository-pattern, domain-driven-design
- Canonical reference: Alistair Cockburn’s “Hexagonal Architecture” article (2005)