Onion Architecture
Jeffrey Palermo's domain-centric layering model where all dependencies point toward the Domain Model at the center, and infrastructure is the outermost ring.
What it is
Onion Architecture, introduced by Jeffrey Palermo in 2008, organizes application code into concentric layers like the cross-section of an onion. From the inside out, the layers are: Domain Model (entities and value objects), Domain Services (domain logic that spans multiple entities), Application Services (use case orchestration), and Infrastructure (UI, databases, external services). The fundamental rule — as in Clean Architecture and Hexagonal — is that dependencies always point inward: outer layers can depend on inner layers, but inner layers never depend on outer layers. The domain model at the center has zero external dependencies. Unlike Clean Architecture's four named rings, Onion Architecture explicitly separates Domain Services (pure domain logic with no external calls) from Application Services (orchestrating domain services and calling infrastructure through interfaces), giving domain behavior a dedicated named layer.
Why it exists
Palermo introduced Onion Architecture to address the same core problem as Clean Architecture and Hexagonal: the database-centric architecture where the data model drives everything. By making the domain model the immovable center, the architecture forces every design decision to ask "does this belong in the domain?" before placing it in a layer. The explicit Domain Services layer is a meaningful addition over simpler two-tier (core/infrastructure) splits: it carves out space for domain behavior that involves multiple entities — pricing calculations, business rule engines, domain event dispatchers — without mixing that logic with application orchestration (transaction management, security, external service calls) in Application Services.
When to use
- DDD-aligned projects where Domain Services (cross-entity domain logic) need a dedicated layer distinct from Application Services.
- Teams that find Clean Architecture's ring decomposition (Entities, Use Cases, Interface Adapters) too coarse and want the explicit Domain Services layer.
- Applications with rich domain logic that spans multiple entity aggregates and benefits from explicit domain service objects.
- Codebases where domain invariants must be enforced purely by domain objects without any infrastructure knowledge.
When not to use
- CRUD applications or those with thin domain logic — the Domain Services layer adds overhead for logic that doesn't exist.
- Teams already using Clean Architecture or Hexagonal — Onion is a close variant; mixing metaphors creates confusion, not clarity.
- Projects where the added layer granularity between Domain Services and Application Services is not needed for the actual complexity level.
Typical architecture
Four concentric rings with dependencies pointing inward. Domain Model and Domain Services form the pure business core; Application Services orchestrate; Infrastructure is the outermost ring.
┌──────────────────────────────────────────────┐
│ Infrastructure (outermost) │
│ (ORM, REST controllers, Email, Queues, UI) │
│ ┌────────────────────────────────────────┐ │
│ │ Application Services │ │
│ │ (Orchestration, Transactions, │ │
│ │ Security, Application Events) │ │
│ │ ┌──────────────────────────────────┐ │ │
│ │ │ Domain Services │ │ │
│ │ │ (Cross-Aggregate Business Logic) │ │ │
│ │ │ ┌────────────────────────────┐ │ │ │
│ │ │ │ Domain Model (innermost) │ │ │ │
│ │ │ │ (Entities, Value Objects, │ │ │ │
│ │ │ │ Aggregates, Repos Iface) │ │ │ │
│ │ │ └────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────┘ │ │
│ └────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
Pros and cons
Pros
- Explicit Domain Services layer gives cross-aggregate domain logic a well-defined home.
- Domain Model is dependency-free — pure, fast unit tests with no mocking required.
- Infrastructure is fully replaceable without touching domain or application logic.
- Aligns naturally with Domain-Driven Design's vocabulary (Aggregates, Repositories, Domain Services).
- The clear layer decomposition gives teams a shared language for code placement decisions.
Cons
- Conceptually very close to Clean Architecture — teams must decide which variant to adopt and stick with it consistently.
- The Domain Services / Application Services distinction can be unclear in practice and requires strong conventions.
- Adds layer overhead for simple features; developers often ask "does this go in Domain Services or Application Services?"
- Mapping between layers (Domain ↔ Application ↔ Infrastructure DTOs) creates conversion boilerplate.
Implementation notes
The key distinction to enforce is between Domain Services and Application Services. A Domain Service contains pure business logic: "calculate the discount for this customer given their tier and order history." An Application Service orchestrates: "load the customer, call the domain service, persist the result, send a confirmation event." Repository interfaces are defined in the Domain Model layer; repository implementations live in Infrastructure. In .NET, this maps cleanly to separate class library projects with explicit project references enforcing the dependency rule. In Java, module-info.java can enforce package dependencies. In TypeScript/Node.js, enforce with ESLint import rules or package-level dependency-cruiser config. Always map between layer-specific data models at layer boundaries — never pass infrastructure DTOs into the domain.
Common failure modes
- Domain Services calling repositories directly: Domain Services should receive domain objects as parameters, not reach out to infrastructure to load them — that's Application Services' job.
- Application Services containing business rules: If an Application Service has an
ifstatement implementing a business rule, that rule belongs in a Domain Service or Entity. - Infrastructure types in Domain Model: Entity Framework, Hibernate, or Jackson annotations appearing on domain entities — the domain model is no longer dependency-free.
- Skipping layer mapping: Passing the same DTO through all four layers — the "DTO that knows too much" anti-pattern.
- Layer confusion: Two developers disagreeing on which layer a piece of code belongs to — indicates missing conventions or a need for an Architecture Decision Record.
Decision checklist
- Is the Domain Model completely free of all framework, ORM, and infrastructure imports?
- Do Domain Services operate only on domain objects passed in as parameters, with no I/O of their own?
- Do Application Services handle orchestration (transactions, event publishing, security) while delegating rules to Domain Services?
- Are Repository interfaces defined in the Domain Model layer, not in Infrastructure?
- Is there a documented convention for which code belongs in Domain Services vs. Application Services?
- Are architecture tests enforcing layer dependency rules in CI?
Example use cases
- .NET enterprise applications: ASP.NET Core projects structured as separate class library projects (Domain, Application, Infrastructure, Web), with project references enforcing the dependency rule — a very common pattern in the .NET community.
- DDD-aligned e-commerce platform: Pricing, discount, and inventory allocation rules live in Domain Services; the checkout Application Service orchestrates across them, calling infrastructure only through interfaces.
- Insurance underwriting system: Complex underwriting rules are pure Domain Services testable without any infrastructure; Application Services manage workflow, notifications, and persistence.
Related patterns
- Clean Architecture — Uncle Bob's closely related concentric ring model; choose one and apply it consistently.
- Hexagonal Architecture — Ports-and-adapters formulation of the same principles; more flexible layer decomposition.
- CQRS — Often applied within Application Services to separate command and query handling paths.
Further reading
- The Onion Architecture: Part 1 — Jeffrey Palermo's original series.
- Common Web App Architectures — Microsoft's .NET guidance covering Onion Architecture.
- Clean Architecture .NET Template — Jason Taylor's widely-used .NET Clean/Onion template on GitHub.