Technology Independence
Swap databases, change UI framework, switch messaging systems - all without touching business logic!
Imagine your video game console:
You can swap adapters (use different TVs, controllers) without changing the console. That’s hexagonal architecture - your business logic is the console, everything else can be swapped!
Think of a universal power adapter for your laptop:
Key Insight: Your laptop doesn’t care which adapter you use - as long as it provides the right voltage/current (implements the port interface), it works! You can travel anywhere and just swap the adapter.
In Software: Your business logic doesn’t care if you use PostgreSQL or MongoDB - as long as they implement the OrderRepository interface, your code works!
Traditional Layered Architecture Problem:
# BAD: Business logic depends on infrastructureclass OrderService: def __init__(self): # Tightly coupled to PostgreSQL! self.db = psycopg2.connect("postgresql://...")
def create_order(self, order): # Business logic mixed with SQL cursor = self.db.cursor() cursor.execute("INSERT INTO orders ...") # What if we want to use MongoDB? Rewrite everything!Problems:
Hexagonal Architecture Solution:
# GOOD: Business logic depends on interfacesclass OrderService: def __init__(self, order_repo: OrderRepository): # Interface! self._repo = order_repo # Could be PostgreSQL, MongoDB, or Mock!
def create_order(self, order): # Pure business logic - no infrastructure! if order.total < 0: raise ValueError("Invalid order") return self._repo.save(order) # Just calls interfaceBenefits:
The shape is arbitrary - Alistair Cockburn chose a hexagon because:
It could be called:
The important part is the concept, not the shape!
1. Business Logic at the Center
2. Ports Define Contracts
3. Adapters Implement Ports
4. Dependencies Point Inward
Without Hexagonal Architecture:
Web Controller → OrderService → PostgreSQL ↓ (tightly coupled!)Problems:
With Hexagonal Architecture:
REST Adapter → OrderService → OrderRepository (port)GraphQL Adapter → ↓ ↓CLI Adapter → (core) PostgreSQL Adapter MongoDB Adapter In-Memory Adapter (tests)Benefits:
Use When:
Don’t Use When:
Real-World Usage:
A port is an interface that defines HOW to interact with the application core.
Two Types:
Primary Ports (Driving Ports)
Secondary Ports (Driven Ports)
An adapter is a concrete implementation of a port for a specific technology.
Two Types:
Primary Adapters (Driving Adapters)
Secondary Adapters (Driven Adapters)
Technology Independence
Swap databases, change UI framework, switch messaging systems - all without touching business logic!
Testability
Test business logic in isolation. Mock all adapters. No need for database or HTTP server in tests.
Flexibility
Add new adapters easily. Support REST and GraphQL simultaneously. Use multiple databases.
Clear Boundaries
Business logic is completely isolated. No accidental dependencies on infrastructure.
| Aspect | Layered Architecture | Hexagonal Architecture |
|---|---|---|
| Structure | Horizontal layers | Center + adapters |
| Dependencies | Top-down through layers | All point inward to core |
| Flexibility | Less flexible | More flexible |
| Infrastructure | Bottom layer | Adapters on outside |
| Testing | Test through layers | Test core directly |
| Swap Components | Harder | Easier |
| Complexity | Simpler | More complex |
| Best For | Traditional apps | Event-driven, multiple interfaces |
Business Logic First
Core is pure business logic with zero infrastructure dependencies. Everything else is just an implementation detail!
Ports Define Contracts
Interfaces (ports) define HOW to interact with the core. Adapters provide specific implementations.
Easily Swappable
Change databases, UI frameworks, or messaging systems without touching business logic. True technology independence!
Testing Paradise
Test business logic without database, HTTP, or any external dependencies. Fast, reliable, isolated tests.