Introduction to Hexagonal Architecture
Hexagonal Architecture, also known as Ports and Adapters, was introduced by Alistair Cockburn. It aims to create loosely coupled application components that can be easily connected to their software environment.
Core Concepts
The Domain (Hexagon Center)
The core business logic lives at the center, completely independent of external concerns. It contains entities, value objects, domain services, and business rules.
Ports
Ports are interfaces that define how the outside world can interact with your domain. There are two types:
- Driving Ports (Primary): Define how external actors use your application (e.g., API controllers)
- Driven Ports (Secondary): Define what your application needs from external services (e.g., repositories)
Adapters
Adapters implement ports to connect your domain to the outside world:
- Driving Adapters: REST controllers, CLI commands, message handlers
- Driven Adapters: Database repositories, external API clients, file system handlers
Implementation Example
Domain Layer
// Domain Entity
public class Order {
public Guid Id { get; private set; }
public OrderStatus Status { get; private set; }
public void Ship() {
if (Status != OrderStatus.Paid)
throw new DomainException("Order must be paid before shipping");
Status = OrderStatus.Shipped;
}
}Driven Port (Interface)
// Port - defined in domain layer
public interface IOrderRepository {
Task<Order> GetById(Guid id);
Task Save(Order order);
}Driven Adapter (Implementation)
// Adapter - in infrastructure layer
public class SqlOrderRepository : IOrderRepository {
private readonly DbContext _context;
public async Task<Order> GetById(Guid id) {
return await _context.Orders.FindAsync(id);
}
public async Task Save(Order order) {
_context.Orders.Update(order);
await _context.SaveChangesAsync();
}
}Application Service
public class ShipOrderUseCase {
private readonly IOrderRepository _repository;
public async Task Execute(Guid orderId) {
var order = await _repository.GetById(orderId);
order.Ship();
await _repository.Save(order);
}
}Benefits
- Testability: Domain logic can be tested without infrastructure
- Flexibility: Swap adapters without changing domain
- Focus: Domain remains pure business logic
- Independence: Technology decisions are deferred
Project Structure
src/
โโโ Domain/
โ โโโ Entities/
โ โโโ ValueObjects/
โ โโโ Ports/
โโโ Application/
โ โโโ UseCases/
โโโ Infrastructure/
โโโ Persistence/
โโโ Api/
โโโ ExternalServices/Conclusion
Hexagonal architecture creates clear boundaries that make your application more maintainable and testable. The initial investment in structure pays dividends as your application grows.