Hexagonal Architecture in Python — Core Concepts

What Hexagonal Architecture is

Hexagonal Architecture was introduced by Alistair Cockburn in 2005 as a way to isolate application logic from external concerns. The formal name is Ports and Adapters. The central idea: your application exposes ports (interfaces) and external systems connect through adapters (implementations).

The three zones

The Application Core

Pure business logic. No knowledge of HTTP, SQL, message queues, or file systems. In Python, this means plain classes and functions — no framework imports.

Ports

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

  • Driving ports (primary) — how the outside world talks to your application. A use case interface is a driving port. The web controller “drives” the application by calling use cases.
  • Driven ports (secondary) — how your application talks to the outside world. A repository interface is a driven port. Your application “drives” the database through this port.

Adapters

Concrete implementations that plug into ports:

  • Driving adapters — FastAPI route handlers, CLI commands, message consumers. They receive external input and call driving ports.
  • Driven adapters — SQLAlchemy repositories, HTTP API clients, email senders. They implement driven ports to connect to external systems.

How it differs from layered architecture

Traditional layered architecture (presentation → business → data) creates a linear dependency chain. Hexagonal architecture breaks this by making all external systems equally distant from the core. The database is not “below” the business logic — it is beside it, connected through a port, just like the web interface.

This symmetry is the key insight. Your application does not have a “top” and “bottom.” It has a center (core) and an outside (adapters).

A Python example

src/
  core/
    models.py          # Domain entities
    ports.py           # Port interfaces (protocols)
    services.py        # Use cases
  adapters/
    driving/
      api.py           # FastAPI routes
      cli.py           # Click commands
    driven/
      postgres_repo.py # SQLAlchemy repository
      stripe_pay.py    # Stripe payment adapter
  main.py              # Wiring / composition root

The core/ directory has zero imports from adapters/. The adapters import from core/ports to know which interfaces to implement.

Why Python is well suited

Python’s structural typing through Protocol makes ports lightweight. You do not need to register adapters in a container or annotate them with decorators. If an object has the right methods, it satisfies the port — duck typing at its best.

Common misconception

“Hexagonal is just another name for Clean Architecture.” They are closely related but not identical. Hexagonal emphasizes the symmetry between driving and driven sides. Clean Architecture adds explicit layer rings (entities → use cases → adapters → frameworks). In practice, most Python projects blend ideas from both.

When to use it

Hexagonal Architecture pays off when:

  • You expect to swap infrastructure (databases, payment providers, message brokers)
  • Multiple entry points exist (API + CLI + worker)
  • You want to test business logic without spinning up infrastructure
  • The domain is complex enough to justify the structural investment

For a simple CRUD API with one database and one entry point, the overhead may not be worth it.

The one thing to remember: Hexagonal Architecture is about symmetry — every external system connects through the same kind of port, making your core logic independent of all of them equally.

pythonarchitectureclean-code

See Also