← Back to Blog
ARCHITECTURE

What Is Hexagonal Architecture? Ports & Adapters Explained

F. Çağrı BilgehanFebruary 12, 202611 min read
hexagonalarchitectureports adaptersclean architecture

What Is Hexagonal Architecture? Ports & Adapters Guide

Does changing your database affect your entire application? Are you locked into a framework? Hexagonal Architecture isolates your business logic from the outside world, making it testable and changeable.

What Is Hexagonal Architecture?

Hexagonal Architecture, defined by Alistair Cockburn in 2005, is also known as Ports & Adapters. The core idea: put business logic (domain) at the center, push external dependencies (DB, APIs, UI) to peripheral adapters.

          ┌──────────────────────┐
          │     Adapters          │
          │  ┌────────────────┐  │
          │  │   Ports         │  │
          │  │  ┌──────────┐  │  │
 Input ──►│  │  │  Domain   │  │  │──► Output
          │  │  │  (Core)   │  │  │
          │  │  └──────────┘  │  │
          │  └────────────────┘  │
          └──────────────────────┘

Why Hexagonal?

Traditional layered architecture creates tight coupling:

Controller → Service → Repository → Database
  Framework    Mixed      DB-bound
  locked       logic      dependent

Hexagonal solution:

HTTP Adapter → [Port] → Domain Logic → [Port] → DB Adapter
REST/GraphQL                                    PostgreSQL/MongoDB
  • Domain has zero external dependencies
  • Adapters are swappable (PostgreSQL → MongoDB, REST → GraphQL)
  • Easy to test — test domain in isolation

Core Concepts

Domain (Core)

Your business rules. No framework, database, or HTTP concepts.

Port

An interface the domain defines for communicating with the outside world.

Adapter

A concrete implementation that fulfills a port interface.

Practical Example: Order System

Ports

// ports/OrderRepository.ts
interface OrderRepository {
  save(order: Order): Promise<void>;
  findById(id: string): Promise<Order | null>;
}

// ports/PaymentGateway.ts
interface PaymentGateway {
  charge(amount: number, token: string): Promise<PaymentResult>;
}

Domain

class OrderService {
  constructor(
    private orderRepo: OrderRepository,
    private payment: PaymentGateway
  ) {}

  async createOrder(items: Item[], paymentToken: string): Promise<Order> {
    const order = Order.create(items);
    
    const result = await this.payment.charge(order.total, paymentToken);
    if (!result.success) throw new PaymentError(result.error);
    
    order.markAsPaid(result.transactionId);
    await this.orderRepo.save(order);
    return order;
  }
}

Adapters

// PostgresOrderRepository.ts
class PostgresOrderRepository implements OrderRepository {
  async save(order: Order): Promise<void> {
    await db.query('INSERT INTO orders ...', [order.id, order.total]);
  }
}

// StripePaymentGateway.ts
class StripePaymentGateway implements PaymentGateway {
  async charge(amount: number, token: string): Promise<PaymentResult> {
    const result = await stripe.charges.create({ amount, source: token });
    return { success: true, transactionId: result.id };
  }
}

Hexagonal vs Other Architectures

| Feature | Layered | Hexagonal | Clean Architecture | |---------|---------|-----------|-------------------| | Domain isolation | Weak | Strong | Strong | | Testability | Medium | High | High | | Complexity | Low | Medium | High | | DB independence | Low | High | High |

Best Practices

  1. Dependency Rule — Dependencies always point inward
  2. No frameworks in domain — Pure TypeScript/Java/Python
  3. Keep ports small — Apply Interface Segregation
  4. Make adapters swappable — Use Dependency Injection
  5. Use domain events — Decouple side effects

Conclusion

Hexagonal Architecture isolates your business logic from technical details, delivering a sustainable, testable, and flexible architecture. It has a complexity cost, but for medium-to-large projects, the long-term payoff is substantial.

Learn Hexagonal Architecture with interactive missions on LabLudus.

Related Posts

What Is a Message Queue? Async Communication with RabbitMQ & Kafka

Message queues explained: RabbitMQ, Apache Kafka, async architecture, pub/sub patterns, and event-driven design for scalable systems.

What Is Software Architecture? A Comprehensive Guide

What is software architecture, why does it matter, and how do you learn it? A deep dive into architectural patterns, quality attributes, and the architect's career path.