Skip to content

Hexagonal Architecture (Ports and Adapters)

Why Does Hexagonal Architecture Exist?

Hexagonal Architecture (Alistair Cockburn) pursues essentially the same goal as Clean Architecture, covered in the previous chapter: isolate the application's core logic from external, volatile details — databases, UI, frameworks, third-party integrations. It's worth understanding as a genuinely earlier, independently-developed articulation of very similar ideas, using its own distinct vocabulary — and it's genuinely useful to be able to compare the two precisely, rather than treating them (as they're often mistakenly treated) as competing, incompatible approaches.

Same underlying goal as Clean Architecture:

Core application logic  →  isolated from  →  external, volatile details

↓ but described with DIFFERENT vocabulary:

"Ports and Adapters" instead of "layers and the Dependency Rule"

The Core Metaphor

The application's core sits in the center — visualized as a hexagon, a shape chosen deliberately just to have multiple, visually equal-looking sides, specifically to avoid the "top vs bottom" hierarchy a traditional layered diagram's top-to-bottom arrangement implicitly suggests.

                    ┌─────────────┐
                    │   Adapter    │
                    └──────┬──────┘
                           │ Port
              ┌────────────┴────────────┐
              │                          │
    Adapter ──┤       APPLICATION        ├── Port ── Adapter
      │        │           CORE           │
      Port      │                          │
              └────────────┬────────────┘
                           │ Port
                    ┌──────┴──────┐
                    │   Adapter    │
                    └─────────────┘

The hexagon shape itself carries no special technical meaning beyond this — it's purely a deliberate visual choice to avoid implying that any one side is somehow more "top-level" than another, unlike a traditional layered stack diagram's built-in vertical hierarchy.


Ports and Adapters

PORT:    an INTERFACE the application core defines — describing
         what it needs from, or offers to, the outside world,
         without knowing or caring how that's actually implemented

ADAPTER: a CONCRETE implementation that plugs into a port, translating
         between the outside world's specific technology and the
         core's own interface
# A PORT — defined by the core, describing what it needs:
class PaymentGateway(Protocol):
    def charge(self, amount: float, card_token: str) -> bool: ...

# ADAPTERS — concrete implementations plugged in from outside:
class StripeAdapter:
    def charge(self, amount, card_token):
        # actual Stripe API calls here
        ...

class FakePaymentAdapter:  # for tests
    def charge(self, amount, card_token):
        return True  # always succeeds, no real API call at all

This is, mechanically, exactly the same pattern as Clean Architecture's interfaces defined by the core and implemented by outer layers — "port" and "adapter" are simply Hexagonal Architecture's own names for the same idea.


Primary (Driving) Ports vs Secondary (Driven) Ports

A genuinely useful, precise distinction worth knowing by name, since it clarifies direction in a way that's easy to gloss over otherwise.

PRIMARY (Driving) Port:   how the OUTSIDE WORLD calls INTO the
                          application — a REST controller adapter
                          calling into a use case through this
                          kind of port

SECONDARY (Driven) Port:  how the APPLICATION calls OUT to the
                          outside world — a database adapter, an
                          email-sending adapter, a payment gateway
                          adapter, all reached through this kind
                          of port
User's HTTP request → REST Adapter → PRIMARY PORT → Application Core

Application Core → SECONDARY PORT → Database Adapter → actual database

Both are simply "ports" in the general vocabulary, but the direction of the call differs — being precise about which kind you're referring to (something calling into the core, versus the core calling out to something) avoids a common, subtle confusion when discussing this pattern.


The Genuinely Practical Payoff — Same as Clean Architecture

Swap a REST adapter for a CLI adapter (a different way for the
outside world to drive the same application core)

↓

Swap a Postgres adapter for an in-memory test adapter (a different
way the core reaches a database)

↓

NEITHER swap requires touching the application core's own logic
at all — the core only ever depends on its OWN port interfaces,
never on any specific adapter's concrete implementation

This is exactly the same reward Clean Architecture provides — fast, isolated testing of the core logic (swap in fake adapters), and genuine flexibility to change infrastructure or entry points later without rewriting business logic.


Comparing Hexagonal Architecture and Clean Architecture Directly

Hexagonal:  "ports and adapters," a hexagon diagram with the core
            at the center

Clean:       concentric layers (Entities → Use Cases → Interface
             Adapters → Frameworks/Drivers), the Dependency Rule
             stated explicitly as "dependencies point inward"

For most practical purposes, these are the same underlying idea, expressed with different vocabulary and a different diagram shape — not two fundamentally different architectural approaches to be debated as rivals. A "port" is functionally the same thing as one of Clean Architecture's interfaces at a layer boundary; an "adapter" is functionally the same thing as a concrete implementation living in an outer layer. Real production systems described as either "hexagonal" or "clean architecture" are, on close inspection, very often doing essentially the same structural thing — the specific terminology a team uses often just reflects which book or blog post they happened to read first, not a genuinely different underlying decision.


Real-World Examples

Many Production Systems Labeled Either Way

Codebases described as "hexagonal" and codebases described as "clean architecture" frequently turn out, structurally, to be doing the same thing — core logic isolated behind interfaces, concrete implementations plugged in from outside — differing mainly in which specific terminology and diagram the team adopted, not in any deep architectural distinction.


Swappable Payment Gateway Adapters

A common, concrete real-world illustration — an application's core defines a PaymentGateway port, with separate adapters for Stripe, PayPal, or a fake test implementation, letting the payment provider be changed (or a new one added alongside an existing one) without touching the core checkout logic at all.


Testing With Fake Adapters Instead of Real External Services

An everyday, practical benefit shared with Clean Architecture — tests run against fast, in-memory or fake adapters (no real database, no real third-party API calls) while production code uses the real adapters, with the application core itself completely unaware of which one it's actually talking to.


Common Interview Questions

What is Hexagonal Architecture (Ports and Adapters)?

An architectural style that isolates an application's core logic from external details by having the core define "ports" (interfaces describing what it needs from or offers to the outside world) and having "adapters" (concrete implementations) plug into those ports from outside — the hexagon shape itself is just a deliberate visual choice to avoid implying a top-to-bottom hierarchy.


What's the difference between a primary/driving port and a secondary/driven port?

A primary (driving) port is how the outside world calls into the application — like a REST controller driving a use case. A secondary (driven) port is how the application calls out to the outside world — like a database or payment gateway adapter. Both are "ports," but the direction of the call differs, and being precise about which one you mean avoids confusion.


How does Hexagonal Architecture relate to (or differ from) Clean Architecture?

They pursue the same underlying goal — isolating core application logic from external, volatile details — using different vocabulary and diagram shapes. A "port" is functionally the same as one of Clean Architecture's interfaces at a layer boundary; an "adapter" is functionally the same as a concrete implementation in an outer layer. For most practical purposes, they're the same idea, not competing approaches.


What's the practical payoff of designing a system this way?

The application core can be tested in complete isolation using fake adapters (no real database, no real third-party API calls needed), and infrastructure — a database, a payment provider, even the way the outside world drives the application (REST vs CLI) — can be swapped later without touching the core business logic at all, since it only ever depends on its own port interfaces.


Should a team debate whether to adopt "Hexagonal" or "Clean" architecture as if choosing between two different approaches?

Not really — since they express essentially the same underlying idea with different vocabulary, the more useful conversation is about whether isolating core logic behind interfaces is worth its complexity for this specific system, not which of the two nearly-identical namings to formally adopt.


Common Mistakes

❌ Treating Hexagonal Architecture and Clean Architecture as fundamentally different, competing approaches.

They express the same underlying idea — isolating core logic behind interfaces implemented by outer, swappable adapters — with different vocabulary and diagram shapes, not two genuinely different architectural philosophies.


❌ Confusing which side (primary vs secondary) a specific port or adapter is on.

A REST controller adapter is primary (driving the application from outside); a database adapter is secondary (the application driving out to it) — mixing these up muddles discussion of the pattern's actual direction of control.


❌ Assuming the hexagon shape itself carries technical meaning.

It's purely a deliberate visual choice to avoid implying a top-to-bottom hierarchy — the number of sides or their specific arrangement in a diagram has no architectural significance beyond that.


Key Takeaways

  • Hexagonal Architecture pursues the same goal as Clean Architecture — isolating core application logic from external, volatile details — using its own vocabulary: ports (interfaces the core defines) and adapters (concrete implementations plugged in from outside).
  • Primary (driving) ports describe the outside world calling into the application; secondary (driven) ports describe the application calling out to the outside world — a useful, precise distinction for discussing direction of control.
  • The practical payoff mirrors Clean Architecture exactly — fast, isolated testing via fake adapters, and the ability to swap real infrastructure later without touching core business logic.
  • For most practical purposes, Hexagonal Architecture and Clean Architecture are the same underlying idea with different vocabulary and diagram shapes, not competing architectural philosophies to be debated as rivals.
  • The hexagon shape itself carries no special technical meaning — it's a deliberate visual choice to avoid the implied hierarchy of a traditional top-to-bottom layered diagram.