Hexagonal architecture (ports and adapters)
Hexagonal architecture, also called ports and adapters, puts the application’s business logic at the center and keeps everything technical (web frameworks, databases, message brokers, third-party APIs) at the edges. The core defines ports: interfaces describing what it needs and what it offers. Adapters implement those ports for a specific technology.
Clean architecture and onion architecture are close relatives built on the same idea: dependencies point inward, toward the business rules.
Diagram
Section titled “Diagram”flowchart LR
rest[REST adapter] --> inport([Inbound port])
cli[CLI adapter] --> inport
subgraph core [Application core]
inport --> logic[Business logic]
logic --> outport1([Repository port])
logic --> outport2([Payment port])
end
outport1 --> pg[Postgres adapter]
outport2 --> stripe[Payment provider adapter]
How it works
Section titled “How it works”- The core knows nothing about technology. No framework annotations, SQL or HTTP in the business logic.
- Inbound (driving) ports describe what the application can do, e.g.
PlaceOrder. Adapters such as REST controllers or message listeners call them. - Outbound (driven) ports describe what the application needs, e.g.
OrderRepository. Adapters such as a database client implement them. - Dependencies point inward. Adapters depend on the core; the core never depends on adapters.
- Business logic is easy to test: swap real adapters for in-memory ones and test without databases or servers.
- Technology can change: replacing a database or framework touches only one adapter.
- Clear intent: the ports document exactly what the application does and depends on.
- Delays technology decisions: the core can be built before the database is chosen.
- More code: interfaces and mapping between core models and adapter models add boilerplate.
- Overkill for simple applications: a basic data-entry app gains little from the extra indirection.
- Learning curve: teams used to framework-first code need time to adjust.
When to use it
Section titled “When to use it”- Domains with rich, valuable business rules that should outlive today’s frameworks.
- Systems that integrate with many external services or may switch providers.
- Teams that want fast, reliable unit tests of business behavior.
When to avoid it
Section titled “When to avoid it”- Thin CRUD applications whose logic is mostly moving data between screens and tables. A layered architecture is simpler.
- Short-lived prototypes.
Common pitfalls
Section titled “Common pitfalls”- Framework types leaking into the core, such as ORM entities or HTTP request objects used as domain models.
- Ports that mirror the technology. A port named
JdbcOrderDaodefeats the purpose; name ports after business needs. - One port per class. Group ports by capability, not by individual method, to avoid an explosion of tiny interfaces.
Related patterns
Section titled “Related patterns”- Layered architecture
- Modular monolith
- Clean architecture
- Dependency inversion principle
Glossary: domain-driven design
Further reading
Section titled “Further reading”- Alistair Cockburn, “Hexagonal Architecture” (original article, alistair.cockburn.us)
- Robert C. Martin, Clean Architecture
Written and edited by Abhinav Taduka, enterprise software architect.