Hexagonal Architecture Explained: Ports and Adapters for Clean Systems

Understand hexagonal architecture (ports and adapters) — how it isolates business logic from infrastructure, with practical code examples and trade-offs.

hexagonal-architectureports-and-adaptersclean-architecturedesign-patternssystem-design

Hexagonal Architecture

Hexagonal architecture (also called ports and adapters) is a design pattern that isolates the core business logic of an application from its external dependencies by defining explicit boundaries through ports (interfaces) and adapters (implementations).

What It Really Means

In a typical application, business logic gets tangled with infrastructure concerns. Your order processing function calls the Stripe API directly, queries the database using raw SQL, and sends Kafka messages. If you want to switch from PostgreSQL to DynamoDB, you have to rewrite your business logic. If you want to test order processing without a running database and Kafka broker, you cannot.

Hexagonal architecture solves this by placing business logic at the center and defining ports — interfaces that describe what the application needs from the outside world and what it offers to it. Adapters implement these interfaces for specific technologies. Your business logic says "I need to save an order" (port). One adapter implements this with PostgreSQL, another with DynamoDB, another with an in-memory store for testing.

The architecture was proposed by Alistair Cockburn in 2005. The hexagonal shape is arbitrary — it simply provides enough sides to illustrate that there are many ports, not just the traditional "top" (UI) and "bottom" (database) layers. The key insight is that the dependency arrow always points inward: adapters depend on the core, never the reverse.

This is closely related to Clean Architecture (Robert C. Martin) and Onion Architecture (Jeffrey Palermo). All three share the same fundamental principle: protect the domain from infrastructure details.

How It Works in Practice

The Three Zones

Core (Domain): Pure business logic. No imports from frameworks, databases, or external APIs. Contains entities, value objects, domain services, and business rules. This is the most stable and valuable part of your system.

Ports: Interfaces that define how the core communicates with the outside world. There are two types:

  • Driving ports (primary): How the outside world uses the core. Example: OrderService.placeOrder(). These are implemented by the core.
  • Driven ports (secondary): What the core needs from the outside world. Example: OrderRepository.save(), PaymentGateway.charge(). These are interfaces the core defines but adapters implement.

Adapters: Concrete implementations that connect ports to real technologies.

  • Driving adapters: REST controllers, CLI handlers, gRPC servers — they call the core through driving ports.
  • Driven adapters: PostgreSQL repositories, Stripe payment gateways, Kafka event publishers — they implement driven ports.

Real-World Example: Payment Processing

A payment processing system might have:

  • Core: Business rules for validating payments, calculating fees, handling refunds
  • Driving ports: REST API, admin dashboard, webhook receiver
  • Driven ports: PaymentGateway, TransactionRepository, FraudDetector, EventPublisher
  • Driven adapters: Stripe adapter, PostgreSQL adapter, ML fraud service adapter, Kafka adapter

Swapping Stripe for Adyen means writing a new PaymentGateway adapter. The core business logic — fee calculation, refund rules, fraud thresholds — does not change.

Implementation

python
python
python
python
python

Trade-offs

When to Use Hexagonal Architecture

  • Complex business logic that must be tested independently of infrastructure
  • Systems that may need to swap infrastructure (database, payment provider, message broker)
  • Long-lived codebases where maintainability matters more than time-to-first-feature
  • Teams that practice test-driven development
  • Microservices where each service has distinct domain logic

When NOT to Use

  • Simple CRUD applications where the business logic is trivial
  • Prototypes and MVPs where speed matters more than architecture
  • Small scripts or utilities with no significant domain logic
  • When the team is unfamiliar with the pattern — the learning curve may slow delivery

Advantages

  • Business logic is fully testable without infrastructure dependencies
  • Infrastructure can be swapped by writing new adapters
  • Clear separation of concerns makes the codebase navigable
  • Domain code is protected from framework churn

Disadvantages

  • More abstractions mean more files and indirection
  • Can feel over-engineered for simple applications
  • Requires discipline to maintain boundaries over time
  • Initial development is slower due to interface definitions and adapter wiring

Common Misconceptions

  • "Hexagonal architecture means lots of interfaces with one implementation" — If you only ever have one implementation of a port, the pattern still provides value through testability (you always have at least two: the real adapter and the test fake). But evaluate whether the abstraction is justified.

  • "It is the same as layered architecture" — Layered architecture has a strict top-to-bottom dependency chain (controller > service > repository). Hexagonal architecture treats all external dependencies equally — the database and the REST API are both adapters at the same level.

  • "The hexagon shape matters" — The hexagonal shape is just a diagram convention. The principle is dependency inversion: the core defines interfaces, and the outside world adapts to them.

  • "You need a framework to implement it" — Hexagonal architecture is a design pattern, not a framework feature. You implement it with plain interfaces and dependency injection. No special library is needed.

How This Appears in Interviews

Hexagonal architecture questions appear in software design interviews:

  • "How do you structure a service to be testable?" — describe ports for external dependencies, in-memory adapters for testing, and the dependency inversion principle. See our system design interview guide.
  • "How would you swap the database in your system?" — explain the repository port pattern and how the core is decoupled from the storage technology.
  • "What is the difference between hexagonal, clean, and onion architecture?" — discuss the shared principle (dependency inversion) and the minor structural differences.
  • Practice with our architecture interview questions.

Related Concepts

GO DEEPER

Learn from senior engineers in our 12-week cohort

Our Advanced System Design cohort covers this and 11 other deep-dive topics with live sessions, assignments, and expert feedback.