175 lines
4.6 KiB
Markdown
175 lines
4.6 KiB
Markdown
# Hexagonal Architecture (Ports & Adapters) – Reference Manual
|
||
|
||
## Overview
|
||
|
||
Hexagonal Architecture, also known as **Ports and Adapters**, is a software design pattern that isolates an application’s **core business logic** from external systems such as user interfaces, databases, frameworks, and third-party services.
|
||
|
||
The application is conceptually placed at the center (often drawn as a hexagon). All communication with the outside world happens through **ports** (abstract interfaces), which are implemented by **adapters**.
|
||
|
||
The key idea:
|
||
> **The domain does not depend on technology. Technology depends on the domain.**
|
||
|
||
---
|
||
|
||
## Fundamental Principles
|
||
|
||
### 1. Business Logic First
|
||
The core domain represents the real business rules and use cases. It must:
|
||
- Be independent of UI, databases, frameworks, and delivery mechanisms
|
||
- Express *what* the system does, not *how* it is delivered
|
||
|
||
### 2. Explicit Boundaries
|
||
All interactions between the core and external systems cross explicit boundaries (ports). This prevents accidental coupling.
|
||
|
||
### 3. Dependency Inversion
|
||
Dependencies always point **inward**, toward the core. External components depend on abstractions defined by the core.
|
||
|
||
---
|
||
|
||
## Core Concepts
|
||
|
||
### Domain (Core)
|
||
The domain contains:
|
||
- Business entities
|
||
- Value objects
|
||
- Use cases / application services
|
||
- Domain rules and policies
|
||
|
||
It contains **no technical concerns** such as HTTP, databases, file systems, or frameworks.
|
||
|
||
---
|
||
|
||
### Port
|
||
A **port** is an abstract interface defined by the core.
|
||
|
||
Ports describe:
|
||
- What the application *needs* from the outside world (output ports)
|
||
- What the application *offers* to the outside world (input ports)
|
||
|
||
Ports are defined in the language of the domain, not infrastructure.
|
||
|
||
Examples (conceptual):
|
||
- “Store an order”
|
||
- “Send a notification”
|
||
- “Execute a checkout use case”
|
||
|
||
---
|
||
|
||
### Adapter
|
||
An **adapter** is a concrete implementation of a port using a specific technology.
|
||
|
||
Adapters translate:
|
||
- External representations → domain concepts
|
||
- Domain requests → external system calls
|
||
|
||
Adapters are replaceable and exist at the system’s edge.
|
||
|
||
#### Adapter Types
|
||
|
||
**Primary (Driving) Adapters**
|
||
- Initiate interaction with the core
|
||
- Examples: Web UI, CLI, REST controller, automated tests
|
||
|
||
**Secondary (Driven) Adapters**
|
||
- Are used by the core
|
||
- Examples: Database repositories, message brokers, email services
|
||
|
||
---
|
||
|
||
## Dependency Rule
|
||
|
||
- The **core defines ports**
|
||
- **Adapters implement ports**
|
||
- The core knows nothing about adapters
|
||
- Adapters depend on the core, never the reverse
|
||
|
||
This rule guarantees that business logic remains stable even when technologies change.
|
||
|
||
---
|
||
|
||
## Interaction Flow
|
||
|
||
1. A primary adapter receives input (e.g., user action)
|
||
2. It calls an **input port**
|
||
3. The core executes business logic
|
||
4. The core calls **output ports** as needed
|
||
5. Secondary adapters fulfill those ports using external systems
|
||
|
||
All I/O stays outside the core.
|
||
|
||
---
|
||
|
||
## Structuring a System
|
||
|
||
A conceptual structure:
|
||
|
||
- Core
|
||
- Domain entities
|
||
- Use cases
|
||
- Port interfaces
|
||
- Adapters
|
||
- Input adapters (UI, API, tests)
|
||
- Output adapters (DB, services, files)
|
||
- Composition root
|
||
- Wires ports to adapters at startup
|
||
|
||
This structure is conceptual, not tied to folders or modules.
|
||
|
||
---
|
||
|
||
## Testing Strategy
|
||
|
||
Hexagonal architecture enables strong testing practices:
|
||
|
||
- Test core logic using fake or in-memory adapters
|
||
- No need for databases or servers in unit tests
|
||
- Integration tests focus on individual adapters
|
||
|
||
Testing becomes simpler because dependencies are explicit and replaceable.
|
||
|
||
---
|
||
|
||
## Benefits
|
||
|
||
- High testability
|
||
- Clear separation of concerns
|
||
- Technology independence
|
||
- Easier maintenance and evolution
|
||
- Multiple interfaces over the same core logic
|
||
- Strong alignment with Domain-Driven Design
|
||
|
||
---
|
||
|
||
## Comparison to Layered Architecture
|
||
|
||
Layered Architecture:
|
||
- Organizes code by technical layers
|
||
- Often allows UI → DB coupling
|
||
- Business logic can leak into infrastructure
|
||
|
||
Hexagonal Architecture:
|
||
- Organizes around the domain
|
||
- Enforces strict boundaries
|
||
- Treats UI and DB as interchangeable details
|
||
|
||
---
|
||
|
||
## When to Use
|
||
|
||
Hexagonal architecture is well suited for:
|
||
- Medium to large systems
|
||
- Long-lived codebases
|
||
- Complex business domains
|
||
- Systems with multiple interfaces
|
||
- Applications that must remain adaptable
|
||
|
||
It may be unnecessary for very small or trivial applications.
|
||
|
||
---
|
||
|
||
## Summary
|
||
|
||
Hexagonal Architecture places the domain at the center and treats all external systems as replaceable plugins. By communicating exclusively through ports and adapters, it ensures long-term flexibility, maintainability, and testability.
|
||
|
||
This pattern is language-agnostic and focuses on **design principles**, not frameworks.
|