Clean Architecture vs Hexagonal (Ports & Adapters): The Complete Practical Guide

Clean Architecture vs Hexagonal (Ports & Adapters): The Complete Practical Guide
Table of Contents
- The Problem: Dependency Creep
- The Dependency Inversion Principle (DIP): The Foundation
- Hexagonal Architecture: Driving vs Driven Ports
- Clean Architecture: The Concentric Circles Explained
- Ports vs Adapters: Implementation in TypeScript
- Java Implementation: Spring with Hexagonal Architecture
- Testing Without Infrastructure
- The Directory Structure: How to Organise the Code
- When Hexagonal/Clean Architecture is Overkill
- Frequently Asked Questions
- Key Takeaway
The Problem: Dependency Creep
In most applications grown organically, the business logic is entangled with infrastructure:
The consequences:
- Unit testing the "empty order" rule requires a real HTTP server AND a real database
- Swapping PostgreSQL for MongoDB requires touching business logic code
- Adding a CLI interface for admin orders would duplicate the business rules
The Dependency Inversion Principle (DIP): The Foundation
The DIP states: High-level modules should not depend on low-level modules. Both should depend on abstractions.
When business logic depends on an interface (not a concrete database), you can swap implementations freely. The database Adapter implements the interface — the domain defines it.
Hexagonal Architecture: Driving vs Driven Ports
Alistair Cockburn's Hexagonal Architecture divides adapters into two categories:
Driving Ports (left side): Interfaces that the outside world uses to call your application (e.g., PlaceOrderUseCase). Driving Adapters implement the callers (REST controllers, CLI commands).
Driven Ports (right side): Interfaces that your application calls to reach infrastructure (e.g., OrderRepository, EmailService). Driven Adapters implement these (PostgreSQL implementation, SendGrid implementation).
Clean Architecture: The Concentric Circles Explained
Uncle Bob's Clean Architecture adds more specificity to the layers inside the hexagon:
Dependency Rule: Source code dependencies can only point inward. The inner circle knows nothing about the outer circles.
| Layer | Contains | Knows About |
|---|---|---|
| Domain/Entities | Business objects, domain logic | Nothing (pure business) |
| Use Cases | Application-specific business rules | Only Domain layer |
| Interface Adapters | Controllers, Presenters, Gateways | Use Cases + Domain |
| Frameworks & Drivers | DB, Web, UI frameworks | Everything |
Ports vs Adapters: Implementation in TypeScript
Testing Without Infrastructure
The payoff of this architecture is effortless testing. The use case has zero dependencies on real infrastructure:
The Directory Structure
Frequently Asked Questions
What's the real difference between Clean and Hexagonal Architecture? Hexagonal is focused on the boundary between the application and the outside world — it explicitly categorises Driving vs Driven ports. Clean Architecture is focused on the internal layering — it adds the Domain/Entities and Use Cases distinction inside the hexagon. In practice, most production architectures use both concepts simultaneously: Clean Architecture's layers internally, Hexagonal's Port/Adapter naming externally.
How is this different from standard Layered (N-Tier) Architecture? In standard layered architecture, the Business Layer calls the Data Access Layer — it depends downward onto database code. In Hexagonal/Clean, the Business Layer defines an interface (Port) and the database implements that interface — the dependency is inverted. This means the business layer has zero knowledge of the database.
Key Takeaway
Clean and Hexagonal Architecture solve the same problem differently (circles vs hexagon), but produce the same outcome: business logic that is completely isolated from frameworks and databases. This isolation makes testing trivial, infrastructure swapping straightforward, and long-term maintainability dramatically better. The cost is real — more interfaces, more adapters, more code. Accept that cost gladly for systems with complex business rules that will live for 5+ years. Skip it for simple CRUD applications where the database is the business logic.
Read next: Microkernel Architecture: Building a Plugin-Based System →
Part of the Software Architecture Hub — comprehensive guides from architectural foundations to advanced distributed systems patterns.
