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:
| Aspect | Hexagonal | Onion |
|---|---|---|
| Primary metaphor | Ports & adapters at the boundary | Concentric layers |
| Internal structure of domain | Not specified | Explicit: Domain Model + Domain Services + Application Services |
| Driving / driven distinction | Central concept | Implicit |
| Visual | Hexagon with adapters | Concentric 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:
| Situation | Use |
|---|---|
| You want to emphasize the external boundary (multiple driving channels, swappable infrastructure) | Hexagonal |
| You want to emphasize internal layering and stability | Onion |
| You want a complete picture that explicitly references both | Clean Architecture |
| You don't have strong feelings | Whichever 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
- Hexagonal Architecture — emphasizes the boundary; Onion emphasizes the layering. Same idea, different focus.
- Clean Architecture — explicitly synthesizes Onion and Hexagonal.
- DDD Tactical Patterns — Onion's Domain Model layer is the natural home for DDD aggregates and value objects.
- Modular Monolith — each module's internal structure is often onion-shaped.
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?