DEV Community

Cover image for Hexagonal Architecture: A Comprehensive Guide
ali ehab algmass
ali ehab algmass

Posted on

Hexagonal Architecture: A Comprehensive Guide

Hexagonal Architecture, also known as Ports and Adapters Architecture, is a software design pattern created by Alistair Cockburn in 2005. It's designed to create loosely coupled application components that can be easily connected to their software environment through ports and adapters.

Core Concept

The fundamental idea is to isolate your business logic from external concerns like databases, user interfaces, external APIs, and frameworks. Your application's core doesn't know or care about these implementation details.

Think of it like a medieval castle (your business logic) surrounded by a moat, with multiple drawbridges (ports) that can be raised or lowered. Each drawbridge connects to different parts of the outside world (adapters), but the castle itself remains independent and protected.

Key Components

1. The Domain/Core (The Hexagon)

This is the heart of your application containing:

  • Business logic and rules
  • Domain models and entities
  • Use cases or application services
  • Domain events

The core has no dependencies on external frameworks, databases, or UI. It defines interfaces (ports) for what it needs from the outside world.

2. Ports

Ports are interfaces that define how the outside world can interact with your application. There are two types:

  • Primary/Driving Ports (Inbound): Define use cases that drive your application. For example, "CreateOrderPort" might be implemented by a use case service
  • Secondary/Driven Ports (Outbound): Define what your application needs from external systems. For example, "OrderRepositoryPort" defines how to save/retrieve orders, but doesn't implement it

3. Adapters

Adapters are concrete implementations that connect external systems to your ports:

  • Primary/Driving Adapters: Trigger application logic (REST controllers, CLI commands, message queue consumers, scheduled jobs)
  • Secondary/Driven Adapters: Implement the interfaces your domain needs (database repositories, email services, payment gateways, external API clients)

How It Works: A Practical Example

Let's say you're building an e-commerce order system:

Domain Core contains:

OrderService (use case)
Order (entity)
OrderRepositoryPort (interface)
PaymentServicePort (interface)
Enter fullscreen mode Exit fullscreen mode

Primary Adapter - REST API:

OrderController receives HTTP requests
Calls OrderService.createOrder()
Returns HTTP response
Enter fullscreen mode Exit fullscreen mode

Secondary Adapters:

PostgresOrderRepository implements OrderRepositoryPort
StripePaymentService implements PaymentServicePort
Enter fullscreen mode Exit fullscreen mode

The beauty here: your OrderService doesn't know it's being called via REST or that orders are stored in Postgres. You could swap REST for GraphQL or Postgres for MongoDB without touching your business logic.

Key Benefits

Testability: You can test your business logic in complete isolation using mock adapters. No need for databases or external services during unit testing.

Flexibility: Swap implementations easily. Start with in-memory storage for prototyping, move to Postgres for production, add Redis caching later—all without changing core logic.

Technology Independence: Your business logic isn't tied to any framework or technology. Frameworks are just implementation details that can be replaced.

Multiple Interfaces: Support different ways to interact with your system simultaneously (REST API, CLI, message queue) without duplicating business logic.

Maintainability: Changes to external systems or technologies don't cascade into your business logic, reducing the risk of breaking core functionality.

Common Structure

A typical project might be organized like this:

/domain
  /entities
  /services (use cases)
  /ports (interfaces)

/adapters
  /primary
    /rest
    /cli
  /secondary
    /persistence
    /messaging
    /external-apis
Enter fullscreen mode Exit fullscreen mode

Dependency Rule

The critical rule: dependencies point inward. The domain has no dependencies on anything external. Adapters depend on ports defined in the domain. This is achieved through dependency inversion—the domain defines interfaces, and adapters implement them.

When to Use It

Hexagonal Architecture shines when you have:

  • Complex business logic that needs protection from external changes
  • Multiple interfaces to your application
  • Need for extensive testing
  • Long-term projects where technology choices may evolve
  • Teams working on different parts of the system independently

For simple CRUD applications with minimal business logic, it might be overkill.

Relationship to Other Patterns

Hexagonal Architecture aligns closely with:

  • Clean Architecture (Uncle Bob): Similar layering and dependency rules
  • Onion Architecture: Concentric layers with similar principles
  • Domain-Driven Design: Often used together, as both emphasize domain modeling

Would you like me to explore any particular aspect in more depth, such as practical implementation examples, testing strategies, or how it compares to other architectural patterns?

Top comments (0)