Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

Hexagonal Architecture (Ports & Adapters)

Your business logic at the center, everything else is just a detail

Imagine your video game console:

  • The console (core): Runs your games with all the game logic
  • Ports: HDMI port, controller ports, power port
  • Adapters: HDMI cable, wireless controller, power adapter

You can swap adapters (use different TVs, controllers) without changing the console. That’s hexagonal architecture - your business logic is the console, everything else can be swapped!

Real-World Analogy: Universal Power Adapter

Section titled “Real-World Analogy: Universal Power Adapter”

Think of a universal power adapter for your laptop:

  • Core (Business Logic): Your laptop’s internal components - CPU, RAM, motherboard
  • Ports: The power input port defines what it needs (voltage, current)
  • Adapters: Different power adapters for different countries/regions
    • US adapter (110V)
    • EU adapter (220V)
    • UK adapter (240V)
    • Car adapter (12V)

Key Insight: Your laptop doesn’t care which adapter you use - as long as it provides the right voltage/current (implements the port interface), it works! You can travel anywhere and just swap the adapter.

In Software: Your business logic doesn’t care if you use PostgreSQL or MongoDB - as long as they implement the OrderRepository interface, your code works!

Traditional Layered Architecture Problem:

# BAD: Business logic depends on infrastructure
class OrderService:
def __init__(self):
# Tightly coupled to PostgreSQL!
self.db = psycopg2.connect("postgresql://...")
def create_order(self, order):
# Business logic mixed with SQL
cursor = self.db.cursor()
cursor.execute("INSERT INTO orders ...")
# What if we want to use MongoDB? Rewrite everything!

Problems:

  • Hard to test (need real database)
  • Hard to swap databases (tightly coupled)
  • Business logic mixed with infrastructure code
  • Can’t run without database running

Hexagonal Architecture Solution:

# GOOD: Business logic depends on interfaces
class OrderService:
def __init__(self, order_repo: OrderRepository): # Interface!
self._repo = order_repo # Could be PostgreSQL, MongoDB, or Mock!
def create_order(self, order):
# Pure business logic - no infrastructure!
if order.total < 0:
raise ValueError("Invalid order")
return self._repo.save(order) # Just calls interface

Benefits:

  • Easy to test (use mock repository)
  • Easy to swap databases (just change adapter)
  • Business logic isolated from infrastructure
  • Can run without database (use in-memory adapter)

The shape is arbitrary - Alistair Cockburn chose a hexagon because:

  1. It has enough sides to draw multiple ports
  2. It’s visually distinct from circles/rectangles
  3. It doesn’t imply any specific number of ports (you can have 3, 6, 10 ports)

It could be called:

  • Octagonal Architecture (8 sides)
  • Polygonal Architecture (many sides)
  • Ports & Adapters Architecture (more accurate name!)

The important part is the concept, not the shape!

1. Business Logic at the Center

  • All business rules, domain entities, use cases live in the core
  • Zero dependencies on external frameworks or libraries
  • Pure, testable code

2. Ports Define Contracts

  • Primary Ports: What the application offers (use cases, APIs)
  • Secondary Ports: What the application needs (databases, external services)
  • Ports are interfaces - they define what, not how

3. Adapters Implement Ports

  • Primary Adapters: Implement how external systems call your app (REST, GraphQL, CLI)
  • Secondary Adapters: Implement how your app calls external systems (PostgreSQL, Redis, Email)
  • Adapters are pluggable - swap them without changing core

4. Dependencies Point Inward

  • Core has zero dependencies on adapters
  • Adapters depend on core (ports)
  • This is Dependency Inversion Principle in action!

Real-World Example: E-Commerce Application

Section titled “Real-World Example: E-Commerce Application”

Without Hexagonal Architecture:

Web Controller → OrderService → PostgreSQL
(tightly coupled!)

Problems:

  • Can’t test without PostgreSQL running
  • Can’t switch to MongoDB without rewriting OrderService
  • Business logic mixed with SQL queries

With Hexagonal Architecture:

REST Adapter → OrderService → OrderRepository (port)
GraphQL Adapter → ↓ ↓
CLI Adapter → (core) PostgreSQL Adapter
MongoDB Adapter
In-Memory Adapter (tests)

Benefits:

  • Test with in-memory adapter (no database needed)
  • Switch databases by swapping adapter
  • Add GraphQL API without changing business logic
  • Business logic completely isolated

Use When:

  • You need to test business logic in isolation
  • You want to swap technologies (databases, frameworks) easily
  • You have multiple interfaces (REST, GraphQL, CLI)
  • You want technology independence
  • You’re building long-lived applications that will evolve

Don’t Use When:

  • Simple CRUD applications with one interface
  • Prototypes/MVPs where speed matters more than flexibility
  • Applications that will never change technologies
  • Team doesn’t understand the pattern (adds complexity)

Real-World Usage:

  • Netflix: Uses hexagonal architecture for microservices
  • Amazon: Many services use ports/adapters pattern
  • Uber: Core business logic isolated from infrastructure
  • Banking Systems: Need to swap databases, add new interfaces

Diagram

A port is an interface that defines HOW to interact with the application core.

Two Types:

  1. Primary Ports (Driving Ports)

    • Purpose: What the application offers to the outside world
    • Direction: External → Core (adapters CALL these)
    • Examples: Use case interfaces, command handlers
  2. Secondary Ports (Driven Ports)

    • Purpose: What the application needs from the outside world
    • Direction: Core → External (adapters IMPLEMENT these)
    • Examples: Repository interfaces, notification interfaces
Diagram

An adapter is a concrete implementation of a port for a specific technology.

Two Types:

  1. Primary Adapters (Driving Adapters)

    • Convert external requests → port calls
    • Examples: REST controller, GraphQL resolver, CLI command
  2. Secondary Adapters (Driven Adapters)

    • Implement ports using specific technology
    • Examples: PostgreSQL repository, SendGrid email sender

4. Use Case Implementation (Application Core)

Section titled “4. Use Case Implementation (Application Core)”

6. Secondary Adapter (PostgreSQL Repository)

Section titled “6. Secondary Adapter (PostgreSQL Repository)”

Technology Independence

Swap databases, change UI framework, switch messaging systems - all without touching business logic!

Testability

Test business logic in isolation. Mock all adapters. No need for database or HTTP server in tests.

Flexibility

Add new adapters easily. Support REST and GraphQL simultaneously. Use multiple databases.

Clear Boundaries

Business logic is completely isolated. No accidental dependencies on infrastructure.


AspectLayered ArchitectureHexagonal Architecture
StructureHorizontal layersCenter + adapters
DependenciesTop-down through layersAll point inward to core
FlexibilityLess flexibleMore flexible
InfrastructureBottom layerAdapters on outside
TestingTest through layersTest core directly
Swap ComponentsHarderEasier
ComplexitySimplerMore complex
Best ForTraditional appsEvent-driven, multiple interfaces

Business Logic First

Core is pure business logic with zero infrastructure dependencies. Everything else is just an implementation detail!

Ports Define Contracts

Interfaces (ports) define HOW to interact with the core. Adapters provide specific implementations.

Easily Swappable

Change databases, UI frameworks, or messaging systems without touching business logic. True technology independence!

Testing Paradise

Test business logic without database, HTTP, or any external dependencies. Fast, reliable, isolated tests.



  • “Hexagonal Architecture” by Alistair Cockburn (original article)
  • “Get Your Hands Dirty on Clean Architecture” by Tom Hombergs
  • “Clean Architecture” by Robert C. Martin
  • “Implementing Domain-Driven Design” by Vaughn Vernon