Steven's Knowledge

Onion Architecture

Concentric layers of stability — the sibling no one introduces, and what it adds to the conversation Hexagonal already started

Onion Architecture

Jeffrey Palermo published The Onion Architecture in a 2008 blog series, proposing what he saw as a corrective to traditional layered architecture. Where conventional N-tier put the database at the bottom (with business logic depending on it), Palermo put the domain at the center and pushed infrastructure to the outside. Dependencies point inward, the same rule that Hexagonal Architecture had stated three years earlier and that Clean Architecture would synthesize in 2012.

The three patterns are close enough — Hexagonal, Onion, Clean — that practitioners often pick whichever name they encountered first. This page is about Onion specifically: where it came from, what its layering adds to the conversation, and why it has not displaced its siblings.

The Layers

Onion Architecture organizes code in concentric rings:

   ┌──────────────────────────────────────────────────┐
   │ Infrastructure / Tests / UI                       │
   │ (concrete dependencies: DB, HTTP, framework)      │
   │                                                    │
   │  ┌────────────────────────────────────────┐      │
   │  │ Application Services                     │      │
   │  │ (orchestration; use cases; transaction   │      │
   │  │  boundaries)                              │      │
   │  │                                            │      │
   │  │  ┌──────────────────────────────────┐    │      │
   │  │  │ Domain Services                    │   │      │
   │  │  │ (operations spanning entities)     │   │      │
   │  │  │                                     │   │      │
   │  │  │  ┌──────────────────────────┐    │   │      │
   │  │  │  │ Domain Model                │   │   │      │
   │  │  │  │ (entities, value objects,   │   │   │      │
   │  │  │  │  domain logic — the core)   │   │   │      │
   │  │  │  └──────────────────────────┘    │   │      │
   │  │  └──────────────────────────────────┘    │      │
   │  └────────────────────────────────────────┘      │
   └──────────────────────────────────────────────────┘
   
   Dependencies point inward only.

The layers:

  • Domain Model (core). Entities, value objects, aggregates, domain invariants. The most stable. Knows nothing about anything else.
  • Domain Services. Operations that span multiple entities or do not naturally belong to one. The discount engine, the pricing rule evaluator. Depend on the domain model.
  • Application Services. Use-case orchestration, transaction management, coordination across domain services. The "this is what the application does" layer.
  • Infrastructure / UI / Tests (outer). Everything concrete: web framework, database, message broker, test runner. All depend inward; nothing in the core depends on them.

What Onion Emphasized

When Palermo wrote, the dominant pattern in the .NET world was N-tier: Presentation → Business Logic → Data Access → Database. In that arrangement, the business logic depended on data access classes, which depended on the database. A schema change rippled up. A database migration was a business-logic change.

Onion's contribution was to make the inversion explicit and central. Business logic does not depend on data access; data access depends on business-logic-defined interfaces. The diagram makes this hard to miss in a way that the older N-tier diagrams did not.

The other emphasis: layers within layers. Onion explicitly separates Domain Model (just types and rules) from Domain Services (operations across entities) from Application Services (use cases). Hexagonal Architecture is less prescriptive about this internal structure; Onion gives a more opinionated answer.

What Onion and Hexagonal Disagree About

Surprisingly little. The differences are emphasis, not substance:

AspectHexagonalOnion
Primary metaphorPorts & adapters at the boundaryConcentric layers
Internal structure of domainNot specifiedExplicit: Domain Model + Domain Services + Application Services
Driving / driven distinctionCentral conceptImplicit
VisualHexagon with adaptersConcentric circles

A pragmatic read: Hexagonal tells you about the boundary; Onion tells you about the internal layering. You can use both ideas in the same codebase — that is essentially what Clean Architecture does.

What Onion Adds in Practice

Three useful contributions specific to Onion:

Application Services as a Distinct Layer

The use-case layer is named explicitly. In Hexagonal you can have use cases as "what driving adapters call into," but the term Application Service gives this a clearer home. The result is fewer use cases that drift into business logic, and fewer entities that grow application coordination responsibilities.

Domain Services as a Mid-Layer

For operations that span aggregates (DDD tactical), Onion has an explicit place: a Domain Service inside the domain ring but outside the entity ring. This makes it easier to organize the "this doesn't belong to one aggregate" code without it becoming an Application Service (which would be too far out) or being smuggled into an aggregate (which would violate single responsibility).

"Layers" Vocabulary

The diagram makes it easy to talk about "moving something inward" or "depending outward." Architectural discussions in Onion teams often borrow this vocabulary even when the codebase is more accurately described as Hexagonal.

What Onion Does Not Add

If you already have Hexagonal Architecture, adopting Onion's vocabulary on top buys you very little. The "internal layering" distinction is largely a matter of folder structure and naming, both of which can be done identically under either name. The reason to know the term is mainly to recognize that "Onion" in someone else's documentation refers to the same architectural style with different signage.

What Onion Costs

Same as Hexagonal and Clean: ceremony, boilerplate, indirection, the discipline cost of keeping inner layers ignorant of outer ones. Applied to simple domains, all three over-engineer.

When to Pick Which

A practical heuristic:

SituationUse
You want to emphasize the external boundary (multiple driving channels, swappable infrastructure)Hexagonal
You want to emphasize internal layering and stabilityOnion
You want a complete picture that explicitly references bothClean Architecture
You don't have strong feelingsWhichever name your team already uses

The substantive choice — "should this codebase have a layered structure with the dependency rule?" — is the same under all three names. The labeling matters less than getting the substance right.

When to Skip All Three

The case for not using any of Clean / Hexagonal / Onion is real and applies more often than the architecture community acknowledges:

  • Simple CRUD. The framework's defaults plus a thin domain.
  • Short-lived code. Prototypes, experiments, throwaway tools.
  • Heavy framework reliance. Where the framework's MVC defaults are well-tuned and replacing them adds friction.
  • Small team, single channel. A 3-person team with one product and one delivery channel does not need this much ceremony.

In all these cases, layered ceremony adds cost without paying back. Recognize it.

Common Mistakes

  • Onion as a folder structure only. Three folders — Domain, Application, Infrastructure — without enforcing the dependency rule. The folder names give the appearance of structure; the dependencies do not match.
  • Domain Services that are actually Application Services. Coordinating multiple aggregates with transaction management belongs in Application Services. If a "Domain Service" is starting transactions or sending notifications, it has crossed a layer.
  • Anemic domain model. Same anti-pattern as in DDD; Onion is no protection against it.
  • Adopting Onion vocabulary without inversion. Calling something an "Infrastructure layer" while letting the domain import from it. The label is meaningless if the dependencies do not respect it.

Relation to Other Pages

Further Reading

  • Jeffrey Palermo, The Onion Architecture (blog series, 2008) — the originals. Four short posts.
  • Jeffrey Palermo, Onion Architecture (book chapter / talks) — extended treatments.
  • Tom Hombergs, Get Your Hands Dirty on Clean Architecture — uses Hexagonal vocabulary but the internal structure he advocates is essentially Onion.
  • Mark Seemann, Dependency Injection Principles, Practices, and Patterns — the .NET-centric companion to Onion's lineage.

Pre-commit Checklist

  • For each module structured as Onion, do dependencies actually point inward — verified by build tooling, not just convention?
  • Are Application Services orchestrating, Domain Services spanning aggregates, and the Domain Model holding entities/invariants — or have these roles bled into each other?
  • Have I avoided treating "Onion" as a folder convention without the dependency discipline?
  • Do I have a specific reason for the layered structure (domain complexity, longevity, multiple channels) or am I applying it by default?
  • Have I considered whether Hexagonal vocabulary or Clean Architecture's synthesis would communicate the same design to my team more clearly?

On this page