Skip to content

Onion and Hexagonal Architecture Together

Core Idea

Examples and diagrams in this page follow the shared Hypothetical Scenario.

Onion Architecture and Hexagonal Architecture are not competing models. They solve different parts of the same design problem. Onion gives dependency direction and policy protection. Hexagonal gives interaction shape through ports and adapters. Used together, they produce a system that stays stable under delivery pressure.

In practice, Onion answers this question. Where should core rules live and which way should dependencies point. Hexagonal answers this question. How should requests enter and external systems connect without leaking infrastructure into core logic.

Historical Context

Onion Architecture was formalized by Jeffrey Palermo in 2008. Hexagonal Architecture was formalized by Alistair Cockburn in 2005. Both models reacted to the same recurring failure in enterprise systems. Teams placed business rules inside framework and persistence code. As products evolved, testing became slow and change cost increased.

Sources: Cockburn (2005), Palermo (2008), and Palermo (2013)

The Problem It Solves

Teams that build large systems often face structural drift. Business rules spread across controllers, ORMs, transport mappers, and queue handlers. A small policy change then touches many files. A framework migration turns into a rewrite. Tests require full infrastructure and become slow.

One model alone helps but leaves gaps. Onion without Hexagonal can define ring boundaries but leave unclear entry and exit contracts. Hexagonal without Onion can define good ports but allow weak dependency direction inside the core. The combined model closes both gaps.

For the hypothetical car platform, these failures are common. Match scoring, ownership cost projection, and marketplace search each touch many external systems. Without strong structure, domain behavior leaks into adapters. That leakage breaks maintainability and slows feature delivery.

Main Concept

The combined model is simple to reason about. Think of Onion as the structural axis. Think of Hexagonal as the boundary axis. One protects policy from detail. The other protects interaction from coupling.

Combined interpretation:

  • Onion center holds domain entities, value objects, and core policies
  • Onion inner application ring orchestrates use cases
  • Hexagonal ports live at core boundaries as explicit contracts
  • Hexagonal adapters live in outer layers and implement those contracts
  • Compile time dependencies point inward
  • Runtime calls enter through inbound adapters and leave through outbound adapters

Onion and hexagonal combined model

Combined view. Rings protect policy boundaries. Ports and adapters protect interaction boundaries.

How It Works

Consider one workflow in the hypothetical scenario. A buyer updates preferences and requests new car matches. The request enters through an HTTP adapter. That adapter calls a driving port for the use case. The use case runs ranking rules and ownership-cost policies. When data is needed, it calls driven ports such as listing repository, market value provider, and deal notifier. Outbound adapters implement those ports.

This path keeps each concern in the right place. Transport parsing stays in adapters. Domain decisions stay in policy code. Storage and API calls stay in outbound infrastructure.

Step flow in combined architecture:

  1. Inbound adapter translates request into a use case command.
  2. Application service runs orchestration inside onion inner ring.
  3. Domain model enforces match and ownership invariants.
  4. Driven ports request listing data and market intelligence.
  5. Outbound adapters call concrete databases or external APIs.
  6. Response adapter maps domain result into API response format.

Combined request flow

Runtime flow view. Control enters from outside. Dependency direction still points toward the core.

Challenges and Shortcomings

The combined model gives strong long-term structure. It also introduces real operational cost. Teams should account for that cost early.

Typical challenges:

  • More interfaces and adapter classes than a framework-first design
  • Higher onboarding effort for engineers new to boundary-driven design
  • Mapping code between transport models and domain models
  • Risk of ceremonial abstractions with no clear domain reason
  • Governance overhead to keep boundaries clean over time

These limits do not invalidate the model. They define where discipline is required. Architecture tests, review checklists, and naming conventions are essential.

Concept Why?
Onion Architecture This page connects directly to Onion Architecture. That page explains ring strategy and inward dependency direction.
Hexagonal Architecture This page connects directly to Hexagonal Architecture. That page explains port types and adapter responsibilities.
Abstraction and Boundaries Ports are explicit abstractions at module boundaries.
Modularity and Composition Use cases compose focused modules through clear contracts.
Dependency Inversion Core policies depend on abstractions and not concrete adapters.
Dependency Injection Composition roots wire the system without leaking infrastructure concerns into core behavior.

Practical Notes

A practical adoption path works best. Start with one critical use case and define clear ports. Add adapters and tests around that use case. Then expand module by module. This path keeps delivery speed stable and prevents architecture freeze.

In multi-service systems, use one combined model per service. Do not share internal domain libraries between services. Share contracts at service edges and keep model ownership local.

Written by: Pedro Guzmán

See References for complete APA-style bibliographic entries used on this page.