Hexagonal Architecture
Ports and Adapters: the application in the middle, every outside thing behind a port. Know the two kinds of port, why the dependency arrows point inward, and how that makes the core testable without a database or a screen.
Overview
Hexagonal Architecture puts the application's logic in the middle and treats everything outside it the same way: the user interface, the database, the message broker, another service, a test. Each of those talks to the core through a port, and a port is served by an adapter that knows one technology. The core knows the ports and nothing else.
Alistair Cockburn described the pattern in 2005 and also calls it Ports and Adapters. His intent sentence is worth learning: the application should be equally drivable by users, programs, automated tests or batch scripts, and be developed and tested in isolation from its eventual run-time devices and databases. The problem it fixes is entanglement, business logic that has HTTP or SQL details mixed into it, so it cannot be tested or reused without those details.
Interviewers use the pattern to check whether you can name the two kinds of port, whether you know which way the dependencies point, and whether you can show the core running in a test with fake adapters. They also like to hear when the pattern is too much for the job.
The shape
Cockburn says the hexagon is not about the number six. It gives room to draw several ports on the edge, and it stops people thinking in one-dimensional layers with the user at the top and the database at the bottom. The only line that matters is inside versus outside.
- Core (the application) — The rules and use cases, with no technology in them — OrderService.place(order)
- Port — A purposeful conversation between the core and the outside, expressed as an interface — Inventory with reserve(sku, qty)
- Adapter — A converter between one technology and one port — SqlInventory, FakeInventory, a Flask route
- Driving (primary) side — Things that start a conversation with the core — UI, tests, schedulers, other services
- Driven (secondary) side — Things the core starts a conversation with — Databases, mail, payment providers
Two kinds of port
Cockburn borrows the words from use cases: a primary actor drives the system, a secondary actor is used by it. A driving port is an interface the core offers; a driven port is an interface the core needs. Both belong to the core. The adapters live outside and depend on them.
- Direction — Outside calls in — Core calls out
- Who implements it — The core — An adapter
- Who calls it — An adapter (controller, CLI, test) — The core
- Test double — A test calls the port directly — A fake or a mock stands in for the adapter
- Naming — A use case: PlaceOrder, CloseAccount — A need: Inventory, Notifier, Clock
A request through the hexagon
The HTTP adapter converts a request into a call and a result into a response. The SQL adapter converts a port call into a query. If the JSON changes, only the first adapter changes; if the database changes, only the last one does.
A runnable core with fake adapters
The core defines its driven port as a Python Protocol and depends only on that. The test drives the core directly, which is what Cockburn means by an automated test being one more driver. No web framework and no database are imported anywhere in this file.
A SqlInventory with the same reserve method would plug into the same service. The service and its test would not change.
Hexagonal, Onion and Clean
Three names describe one rule: source code dependencies point toward the business logic. They differ in how much structure they prescribe inside the core.
- Picture — A hexagon with ports on its edge — Rings: domain model, domain services, application services, then infrastructure, UI and tests outside — Rings: entities, use cases, interface adapters, frameworks and drivers
- Rule — Adapters depend on ports; the core depends on nothing outside — Code may depend on more central layers, never on outer ones — Source code dependencies can only point inward
- Says about the inside — Little; only inside versus outside — Splits the core into three rings — Splits the core into entities and use cases
- Names the outside — Adapters, driving and driven — Infrastructure, UI, tests — Interface adapters, frameworks and drivers
Martin's Clean Architecture post lists Hexagonal and Onion among the ideas it combines, and Palermo's Onion post points to Cockburn. In an interview, say they agree on the rule and pick one vocabulary; mixing them mid-answer sounds unsure.
Where it goes wrong
- A port named after a technology — DatabaseConnector, KafkaPublisher in the core — Name the need: Inventory, EventPublisher
- The core imports an adapter — from adapters.sql import SqlInventory inside a use case — The core imports only its own ports; wiring happens outside
- Framework types cross the port — A use case takes an HTTP request object — The adapter converts to plain arguments first
- One port per table — UsersPort, OrdersPort, OrderLinesPort — One port per conversation the core needs
- Hexagon for a CRUD form — Five files to save one row — A plain layered module; the pattern pays off when rules exist
What you will be able to do
- Give Cockburn's intent in one sentence and say what entanglement it removes.
- Draw the hexagon with driving adapters on one side and driven adapters on the other, and name both kinds of port.
- Explain why both ports belong to the core and which way the dependency arrows point.
- Show a test that drives the core with a fake adapter and no framework.
- Place Hexagonal, Onion and Clean in one table and say what they share.
Interview questions
- What is the difference between a driving port and a driven port?
- How does Hexagonal Architecture improve testability?
- How do you enforce the dependency rule in a codebase?
- How does Hexagonal Architecture relate to Clean Architecture and Onion Architecture?
- When is Hexagonal Architecture the wrong choice?
Sign up free to read the full topic
More Low-Level Design topics
Start free on Cariara · Coding problems · System design studio