Business rules tend to outlast the UI framework and the data access library wrapped around them. Onion architecture was built for that situation, mostly in long-lived business applications where the UI and the data access technology change more often than the rules do.
The idea fits in a sentence. The domain model goes in the middle, layers wrap it, and dependencies only point inward. Persistence, the UI and other infrastructure sit on the outer edge and depend on the core, never the reverse.
Jeffrey Palermo introduced the pattern in a blog series that began on July 29, 2008.
His fourth tenet, published in part 3 on August 4, 2008, works as a test too: all application core code can compile and run separately from infrastructure.
What is onion architecture?

Palermo gave the pattern its name in a blog series, with part 1 published on July 29, 2008 and part 3 on August 4, 2008. A fourth post titled After Four Years followed later. The core idea is the one above: the domain model at the center, dependencies pointing only inward, and infrastructure, persistence and the UI depending on the core while the core depends on none of them.
He didn’t claim to have invented any of it. In part 3 Palermo says he claims no breakthrough and credits earlier industry work, so what he added was a name. A named pattern gives teams a shared vocabulary, which counts for more than it sounds.
Why traditional layers fall short

Palermo’s starting point is the coupling in the classic top-down stack. In a conventional layered architecture, each layer depends on the one beneath it, so the UI ends up depending on data access through transitive dependencies.
Data access is the worst offender because it changes most often. His own estimates of how often differ between posts.
- In part 1 (July 29, 2008) he says data access techniques change at least every three years
- By part 3 (August 4, 2008) it has become every two years or so
Both numbers are the author’s judgment, not measured data. The argument survives either one: a core coupled to data access gets reworked every time data access changes.
Why the database sits outside the core
Part 1 treats the database as external, not as the center. It’s a storage service that the application reaches through infrastructure code implementing an interface the core defines.
In this view there are no “database applications,” only applications that happen to use a database. He ties that decoupling to lower maintenance cost over the life of the application.
The layers of onion architecture

Only the domain model has a fixed position, at the center. The number of rings around it varies. Palermo’s diagrams put UI, infrastructure and tests on the outer edge.
| Layer | Holds | Depends on |
|---|---|---|
| Domain model | Entities and value objects, state with behavior | Only itself |
| Domain services | Domain-defined processes, repository interfaces | Domain model |
| Application services | Use cases, workflow orchestration, transaction boundaries | Domain services, domain model |
| Outer edge | UI, infrastructure, tests | Any inner layer |
The center holds state and behavior together. Teams that already model with domain-driven design place their entities and value objects in this ring.
How many rings does the pattern need?
The published layerings don’t agree on count or names.
- Palermo’s 2008 version has the domain model, a first ring holding repository interfaces, as many extra core rings as needed, and then UI, infrastructure and tests at the edge
- The C# Corner walkthrough settles on four layers, covering domain entities, repository, service and UI
- DZone’s explainer lists domain model, domain services, application services and an outer layer
Treat the direction of dependencies as the fixed part and the ring count as a design choice.
What belongs on the outer edge
Palermo defines infrastructure as commodity code that gives the application no competitive advantage.
Data access and file I/O qualify, along with web service clients and the UI framework.
Tests sit out there as well. They depend on the core, while the core never depends on them.
What does the dependency rule require?

Source code dependencies point toward the center and never outward. Palermo states the rule in part 1: code can depend on layers more central, but not on layers further out.
The domain model therefore depends only on itself.
What the rule forbids
An entity that calls a SQL client or ORM directly is the standard violation. Business logic reading a web request object is the same thing aimed at the UI. Subtler, and easy to miss in review, is a domain service that references a repository class instead of its interface, which is one ring reaching into another.
A quick check is that the core project compiles with no reference to infrastructure projects or data access packages. That is Palermo’s fourth tenet in practice, since core code has to compile and run apart from infrastructure.
What the rule allows
In a traditional layered architecture a layer calls only the layer directly beneath it. Onion architecture is looser about that. Any outer layer can call any inner layer, which Palermo names in part 3 as a key difference from layered designs.
References inside one ring are fine too. In Palermo’s sample application, CodeCampServer, the SpeakerController can use UserSession directly because both share a layer, but it cannot use ConferenceRepository, which sits in another.
Compile time versus run time
The rule governs source-level coupling, not what the running system needs.
At run time the application still needs a database. The core just does not know which one.
How interfaces, repositories and dependency injection enforce the rule
The core declares the interfaces it needs and outer layers implement them. A container hands over the implementations at run time. That is the dependency inversion principle applied to whole layers, and Palermo says the pattern relies heavily on it.
Where repository interfaces live
Repository interfaces belong in the application core, in the first ring around the domain model. The saving behavior stays outside because it involves a database, and only the contract enters the core.
Take IConferenceRepository. It’s defined in the core, in the terms of the domain. ConferenceRepository, the implementation, lives in infrastructure and is coupled to one data access method. Controllers and services only ever see the interface.
Which ORM sits behind the interface is invisible to the core. Entity Framework Core and a hand-written SQL layer look the same from the inside.
Every outward need costs one interface, and CodeGuru lists heavy interface use among the drawbacks.
Where the container is configured
Palermo does not list an IoC container among the four tenets. In part 3 he says that in a mainstream language such as Java or C#, a container makes the pieces fit together easily, and that some languages have such features built in. For C#, he recommended Castle Windsor and StructureMap in 2008.
As of October 2026, Microsoft Learn describes the dependency injection that ships with .NET as a built-in part of the framework and a technique for achieving Inversion of Control.
You register services in an IServiceCollection and resolve them through an IServiceProvider. Each one gets a lifetime of transient, scoped or singleton.
The registrations live in the host project, the composition root, because only the outermost project can reference both the core and the infrastructure.
Microsoft’s guidance warns against resolving a scoped service from a singleton through constructor injection, because the scoped service then behaves like a singleton. A scoped repository injected into a singleton hits exactly that trap.
Palermo’s part 2 (July 30, 2008) walks through the wiring in CodeCampServer.
- SpeakerController asks, through its constructor, for IConferenceRepository, IUserSession and IClock
- All three interfaces are defined in the application core
- ConferenceRepository and UserSession implement them from outer layers
- At run time the container looks up its registry, builds the implementations and passes them in
Loose coupling comes from that last step. No class in the core ever names an implementation.
How does onion architecture differ from n-tier, hexagonal and clean architecture?

Onion, hexagonal and clean architecture share one idea: business logic sits at the center and infrastructure depends on it. They differ in vocabulary and in how much structure they prescribe, while traditional layered designs point dependencies the other way.
| Pattern | Origin | Vocabulary | Dependency direction |
|---|---|---|---|
| Layered (n-tier) | Traditional | UI, business logic layer, data access layer | Top to bottom at compile time |
| Hexagonal | Cockburn, 2005 | Ports and adapters | Toward the application, through ports |
| Onion | Palermo, 2008 | Domain model, concentric rings | Inward, toward the domain model |
| Clean | Martin, 2012 | Entities, use cases, interface adapters, frameworks | Inward, per the Dependency Rule |
Onion versus layered (n-tier)
Microsoft Learn draws a line between the two terms. Layers separate concerns inside the application, and tiers are separate deployment targets. An N-layer application is quite commonly deployed to a single tier.
So the fair comparison is onion architecture against N-layer design. The cost of the downward direction, per the same guide, is that business logic depends on data access details, and testing it often requires a test database.
Onion versus hexagonal
Alistair Cockburn dated his ports and adapters report September 4, 2005, almost three years before Palermo’s series.
The intent of hexagonal architecture is to let one application be driven equally by users, programs, automated tests or batch scripts, and to be developed in isolation from its run-time devices and databases.
Palermo’s part 1 names the premise the two share: externalize infrastructure and write adapter code so the infrastructure never becomes tightly coupled.
Hexagonal treats everything outside the application symmetrically, with one port for each purposeful conversation. Onion cares more about the inside and structures it as rings around the domain model.
Cockburn calls the port count a matter of taste and favors two to four, with four the most he has encountered.
Onion versus clean
Robert C. Martin’s post of August 13, 2012 lists onion architecture among several similar designs and says they share one objective, the separation of concerns.
Martin’s own take, clean architecture, keeps the same inward direction and puts more structure on top. It names four circles (entities, use cases, interface adapters, and frameworks and drivers), states the Dependency Rule formally, and adds a boundary rule: simple data structures cross circles, never entities or database rows.
He also calls the four circles schematic and allows more, which matches Palermo’s variable ring count.
Where the sources disagree
Microsoft Learn folds the family into one architecture with several names (hexagonal, ports-and-adapters, onion, clean) and uses Clean Architecture as its label.
Palermo’s part 1 and Martin’s post treat them as separate patterns with a shared premise.
In practice, pick by the vocabulary the team already uses. Teams that think in rings around a rich domain model will find onion natural. Hexagonal makes more sense when one application has several drivers, and clean is the better pick if use cases should be first-class code.
Laying out an onion architecture solution in .NET

A .NET solution maps the rings to projects. The core project has no outward references, an infrastructure project references the core, and a web host wires the two together. Test projects sit outside, as they do in Palermo’s diagrams.
| Project | Can reference | Must not reference |
|---|---|---|
| Core | Nothing outside itself | Infrastructure, web host, test projects |
| Infrastructure | Core | Web host (creates a cycle with the host) |
| Web host | Core, plus Infrastructure for registration only | Infrastructure types outside the composition root |
| Test projects | Core for unit tests, Infrastructure for integration tests | Nothing references them |
What each project holds
The core holds entities, interfaces and domain services, including the interfaces for data access, file system access and network calls.
Infrastructure gets the Entity Framework Core DbContext, migrations, repository implementations, and services such as a file logger or SMTP notifier.
Controllers, filters, middleware, views and the startup code in Program.cs live in the web host.
Microsoft’s eShopOnWeb reference application follows this split under the name Clean Architecture. Its repository targets ASP.NET Core 8.0 and was archived on January 13, 2025, with a community-supported version continuing at NimblePros.
Building the solution in order
- Create the core project and give it no references to infrastructure projects or data access packages
- Define the repository and service interfaces in the core, next to the entities
- Add the infrastructure project, reference the core, and implement those interfaces there
- Create the web host, reference the core, and register each implementation against its interface in Program.cs
- Add unit tests against the core and integration tests against the infrastructure implementations
Microsoft notes that the host often needs a reference to the infrastructure project to do the wiring. A custom container that loads types from assemblies removes it, and otherwise the guidance is to confine infrastructure type references to the composition root.
How the layout breaks

A controller that calls new on a repository class breaks the layout, and so does a static method call on an infrastructure type. Microsoft’s guidance rules both out in the UI project.
The host reference is another weak spot. Once the web host references infrastructure for wiring, nothing in the compiler stops other host classes from using infrastructure types.
And then there’s the container, which people sometimes treat as mandatory. Palermo recommended one for C# in 2008, but his part 4 of August 19, 2013 says the architecture works fine without StructureMap or Castle Windsor. The simpler sample he points to there leaves out both the container and a UI input form.
When is onion architecture worth using?

It pays off in long-lived business applications with complex behavior, and it costs more than it returns in small websites. Palermo draws the same line in part 1 of his series: he calls the architecture inappropriate for small websites and appropriate for long-lived business applications and applications with complex behavior.
Microsoft Learn lands near the same line from the other side. It says single-unit deployment serves many internal and smaller public applications very well, while most non-trivial business applications benefit from logical layers.
Neither source gives a size threshold, so the call stays a judgment about domain complexity and expected lifespan.
What the core gains
Martin’s 2012 post lists the shared payoff: systems independent of frameworks, UI and database, with business rules testable without a UI, database or web server.
Microsoft’s guide shows what that looks like in a .NET solution: unit testing of the core in isolation, with integration tests aimed separately at the infrastructure implementations.
Palermo adds that code in any outer layer can change without touching the application core, which is where long-term maintainability comes from.
Pros and cons
On the plus side, core logic tests without a database, framework or UI. Infrastructure swaps without core changes, the code stays independent of frameworks, and dependencies run in one clear direction.
The downsides are a learning curve for beginners, more projects and interfaces than a layered design, and responsibilities that are easy to split badly between layers. CodeGuru names the learning curve and the poorly split responsibilities among the drawbacks. The interface count is what I’d expect to grate first.
Where it breaks down
A boundary that nothing checks drifts. Cockburn’s 2005 report describes the failure in layered designs: a team adds a layer promising no business logic, has no mechanism to detect violations, and finds years later that the layer is cluttered with business logic.
The rings of an onion are exposed to the same drift, since nothing inside a project stops a class from reaching for a type it should not know.
Small internal tools are the clearest wrong fit. A layered or single-unit design is the right call there, and the extra interfaces would only add ceremony.
Onion Architecture FAQ
Do you need domain-driven design to use onion architecture?

No. Palermo’s part 4 (August 19, 2013) says onion architecture works well with and without DDD patterns, because the pattern only requires that the core object model accept no dependencies on less stable code.
Is onion architecture suitable for microservices?
Yes, when each microservice is viewed in isolation. Expedia Group Technology’s engineering write-up applies the pattern that way, so each service has its own set of layers around its own domain model.
How does onion architecture fit with CQRS?
CQRS fits without changing the rings. Palermo’s part 4 says the pattern works well with CQRS, forms over data and DDD, since it constrains the direction of dependencies and nothing else.
Can Entity Framework Core entities double as domain entities?
Yes, in Microsoft Learn’s layout, which lists persisted entity classes in the application core and puts the DbContext and migrations in infrastructure. The core still cannot reference EF Core packages.
Martin’s clean architecture post takes the stricter line: entities and database rows do not cross boundaries.
Where do DTOs and mapping code belong?
DTOs that the core’s interfaces need live in the core, as long as they carry no UI or infrastructure dependencies (Microsoft Learn).
Mapping code lives outside the core. In Martin’s scheme, the interface adapters layer converts data between the form the use cases want and the form the database or web needs.
Does onion architecture depend on a particular language?
No. Palermo’s part 3 names both Java and C# as mainstream languages where an IoC container helps wire the pieces together, and adds that some languages have such features built in.
The four tenets talk about interfaces and layers, not a platform.
Keeping the Dependency Rule Intact as the Codebase Grows
The pattern stays intact when a violation fails the build, not when reviewers happen to remember the rule. Two mechanisms catch an outward dependency the day it appears: the core’s reference list and a core test project that runs with no database or web server.
Start with the cheapest checks.
- The core’s references should show no infrastructure projects or data access packages
- The core tests should run with no database or web server
- In the host, infrastructure types belong only in the composition root, though the compiler can’t enforce that one
Cockburn’s 2005 report used automated tests that run the application without its UI to expose leaked business logic, the same signal the core test project gives.
Reference checks work only at project boundaries: in a single project, Microsoft Learn notes, folders give no clear sign of which classes should depend on which.
A continuous integration run that builds the solution and executes the core tests gates every commit on both signals.
- What Communications Teams Should Ask Before Commissioning a Mobile App - November 12, 2026
- How to Install Node JS in VS Code and Run Your First Script - November 11, 2026
- Protecting Users and Brand Standards Across Image-Driven Platforms - November 10, 2026



