Sign inGet started
Skills / Software-architecture

Onion Architecture

The fundamental rule is that all code can depend on layers more central, but code cannot depend on layers further out from the core. This rule is the Dependency Rule.

Onion Architecture is a software architectural pattern that organizes code into concentric layers with the Domain Model at the core, surrounded by Domain Services, Application Services, and Infrastructure at the outermost ring. Its governing rule is absolute: all source code dependencies must point inward — outer layers depend on inner layers, never the reverse. Unlike traditional N-Tier architecture where the database drives design, Onion Architecture makes the domain model king and treats persistence, UI, and external services as swappable implementation details.

By Jeffrey Palermo · Free
Specimen 01 · Live diagnosisOnion Architecture
Input

“I'm building an e-commerce API in C#. Right now my service classes directly call Entity Framework DbContext, which feels wrong but I'm not sure how…”

Diagnosis
Welcome.
Full transcript ↓
Calibrated referenceagent-skills.ai
The gap

Put business logic at the center — make infrastructure a detail, not a foundation

Onion Architecture structures applications as four concentric rings: the Domain Model at the center (entities, value objects — zero external dependencies), Domain Services surrounding it (domain logic that spans multiple entities, domain interfaces), Application Services outside that (use-case orchestration, IRepository calls), and Infrastructure/UI/Tests at the outermost ring (database implementations, controllers, IoC container wiring). The pattern's single governing constraint — the Dependency Rule — states that source code dependencies can only point inward. Infrastructure implements interfaces defined by inner layers (Dependency Inversion Principle), meaning a repository interface lives in the domain, while its SQL implementation lives at the edge. This makes inner layers fully unit-testable without a database, framework, or external service, and makes infrastructure components independently swappable. The pattern contrasts sharply with traditional N-Tier/Layered Architecture, which places the database at the center and causes business logic to be contaminated by persistence concerns.

The problem

Traditional N-Tier applications place the database at the center of design, causing business logic to depend on persistence frameworks, ORM types, and infrastructure specifics. This tight coupling makes business rules hard to test without a real database, infrastructure changes break domain logic, and the application cannot be reasoned about without understanding the data model. Developers building with Onion Architecture recognize this as the foundational mistake: infrastructure should serve the domain, not define it.

The solution

Stop letting your database dictate your domain model. Apply Palermo's Dependency Rule and every layer of your application becomes independently testable, infrastructure becomes swappable, and your business logic finally lives at the center where it belongs.

You bring
  • A description or diagram of your application's domain and use cases
  • Existing code snippets or project structure you want reviewed
  • Your target language or ecosystem (C#/.NET, Java, Python, etc.)
  • Questions about where a specific class, concern, or dependency belongs
You get
  • A correctly layered folder/namespace/project structure aligned to Palermo's four-ring model
  • A dependency audit identifying where your codebase violates the inward-only rule
  • Interface and implementation split decisions for every infrastructure concern
  • A refactoring sequence to migrate a database-centric layered app toward Onion Architecture
In action

Watch the methodology work.

Three specimens from a single real session: the same situation, unaided and calibrated, the full transcript, and the skill answering live in the channel where the work happens.

Fig.01The same situation, two ways
Specimens A · B
Specimen AVague
Unaided

“Your `OrderService` directly instantiates `AppDbContext`, your Domain entities have `[Column]` EF annotations on them, and your data access layer is referenced by half the projects in the solution. Unit testing any business logic requires spinning up an in-memory database, and the last time the team switched connection pooling libraries it broke three service classes.”

×Vague — nothing concrete to act on.
Specimen BCalibrated
With Onion Architecture

“Your Domain Model has zero project references — entities are plain C# with no ORM annotations. `IOrderRepository` is defined in Domain Services, implemented in Infrastructure, wired in `Program.cs`. The entire business logic layer is unit-tested with mock repositories in milliseconds. When the team migrates from EF Core to Dapper, only one Infrastructure class changes — no domain or application logic is touched.”

Specific, grounded, and ends with one clear next step.
Fig.02A real session, unedited
In-app · assistant
Onion ArchitectureClaude
Onion Architecture
Welcome. I apply Jeffrey Palermo's Onion Architecture — the pattern that puts your domain model at the center and makes infrastructure a detail. To get started, tell me about your situation: are you designing a new application, auditing an existing codebase for dependency violations, or trying to place a specific class or concern in the right layer?
I'm building an e-commerce API in C#. Right now my service classes directly call Entity Framework DbContext, which feels wrong but I'm not sure how to fix it.
That's the classic violation Onion Architecture was designed to solve. When your Application Services (or Domain) directly reference `DbContext`, your business logic has a hard dependency on an infrastructure concern — reversing the dependency rule. Here's the fix in Palermo's model:
Define the interface in the inner layer (Domain Services):```csharp// Domain Services layer — no EF referencepublic interface IOrderRepository{ Order GetById(Guid orderId); void Save(Order order);}```
Implement it in the outermost Infrastructure layer:```csharp// Infrastructure layer — EF lives herepublic class EfOrderRepository : IOrderRepository{ private readonly AppDbContext _context; // ...}```
Your Application Services then take `IOrderRepository` via constructor injection — they never see EF. What does your current service look like? Share a snippet and I'll show you the exact refactoring.
Here's a simplified version: public class OrderService { private readonly AppDbContext _db; public void PlaceOrder(Guid customerId, List<LineItem> items) { var customer = _db.Customers.Find(customerId); var order = new Order(customer, items); _db.Orders.Add(order); _db.SaveChanges(); } }
Good — I can see exactly what's happening. `OrderService` is an Application Services layer class, but it reaches directly into Infrastructure (`AppDbContext`). That's an outward dependency, which violates the Dependency Rule. Here's the Onion-correct version across all three layers:
Domain Model layer (no dependencies):```csharppublic class Order{ public Order(Customer customer, List<LineItem> items) { ... }}```
Domain Services layer (defines the contract, no EF):```csharppublic interface ICustomerRepository { Customer GetById(Guid id); }public interface IOrderRepository { void Save(Order order); }```
Application Services layer (orchestrates, depends only on domain interfaces):```csharppublic class OrderService{ private readonly ICustomerRepository _customers; private readonly IOrderRepository _orders;
public OrderService(ICustomerRepository customers, IOrderRepository orders) { _customers = customers; _orders = orders; }
public void PlaceOrder(Guid customerId, List<LineItem> items) { var customer = _customers.GetById(customerId); var order = new Order(customer, items); _orders.Save(order); }}```
Infrastructure layer (implements interfaces, references EF):```csharppublic class EfOrderRepository : IOrderRepository{ private readonly AppDbContext _db; public void Save(Order order) { _db.Orders.Add(order); _db.SaveChanges(); }}```
The IoC container (at the outermost entry point — your `Program.cs`) wires `IOrderRepository → EfOrderRepository`. `OrderService` now has zero knowledge that EF exists. Want me to generate the full project folder structure for your e-commerce API?
Yes, and should tests go in their own project? Where do they fit in the onion?
Tests live at the outermost ring — the same layer as Infrastructure and UI. In Palermo's model, tests are explicitly part of the outermost layer because they depend on the application core but the application core does not depend on them. Here's your complete project structure:
```Ecommerce.sln├── src/│ ├── Ecommerce.Domain/ ← Domain Model layer│ │ ├── Entities/ (Order, Customer, LineItem)│ │ └── ValueObjects/│ ││ ├── Ecommerce.Domain.Services/ ← Domain Services layer│ │ └── Interfaces/ (IOrderRepository, ICustomerRepository)│ ││ ├── Ecommerce.Application/ ← Application Services layer│ │ └── Services/ (OrderService, CartService)│ ││ └── Ecommerce.Infrastructure/ ← Infrastructure layer (outermost)│ ├── Persistence/ (EfOrderRepository — EF lives here)│ ├── ExternalServices/ (PaymentGateway, EmailService)│ └── DependencyInjection/ (IoC wiring: IOrderRepository → EfOrderRepository)├── api/│ └── Ecommerce.Api/ ← UI layer (outermost, depends on Application)│ └── Program.cs (entry point — IoC container registered here)└── tests/ ├── Ecommerce.Domain.Tests/ ← Unit tests (no infrastructure needed) └── Ecommerce.Application.Tests/ (mock IOrderRepository, pure business logic tests)```
Key point: `Ecommerce.Domain` has zero project references. `Ecommerce.Domain.Services` references only Domain. The dependency graph is strictly inward at every level.
This makes so much more sense than how we've been doing it. Our Domain project currently references our Data project for the EF models.
That's the most common and most damaging violation — and exactly what Onion Architecture was created to prevent. When your Domain references your Data/EF project, every EF model change can break domain logic, you cannot unit-test your domain without a database, and your business rules are structurally coupled to your persistence choice forever. The fix is the interface extraction I showed you: define `IOrderRepository` in Domain.Services, delete the EF reference from Domain, and move the EF types into Infrastructure where they belong. That one structural change unlocks pure domain unit tests, lets you swap databases without touching business logic, and makes your domain the independent core Palermo's pattern requires. Want me to generate a Dependency Violation Report for your current project structure so you have a prioritized list of what to fix first?
Reply to Onion Architecture
What the skill is doing
01Layer Classification
02Dependency Rule Audit
03Interface/Implementation Split
Fig.03The same skill, in the channel you already use
WhatsApp · live
9:41
OAOnion Architecturetyping…
Messages are end-to-end encrypted. No one outside of this chat can read them.
Today
I'm building an e-commerce API in C#. Right now my service classes directly call Entity Framework DbContext, which feels wrong but I'm not sure how to…9:14✓✓
I apply Jeffrey Palermo's Onion Architecture — the pattern that puts your domain model at the center and makes infrastructure a de…9:14
Message

The same skill, where the work happens.

No new app to learn. The methodology runs over the WhatsApp Business API, so the answer lands as a reply in the thread you’re already in — same rigour, zero context-switch.

Reads the situation, names the pattern, returns one concrete next move.
Delivered in seconds, inside a conversation that already exists.
Specimen · WhatsApp Business API · live
Capabilities

What it does, specifically.

Each capability is a distinct move drawn straight from the source methodology — not a generic assistant guessing.

CapabilityC-01

Layer Classification

Given any class, interface, module, or concern, determines which of Palermo's four concentric layers it belongs in and explains the reasoning based on the Dependency Rule. Catches common misplacements like repository implementations in the domain layer or domain logic leaking into Application Services.

Based on Palermo's original definitions: Domain Model (entities, value objects), Domain Services (domain interfaces, cross-entity logic), Application Services (use-case orchestration), Infrastructure (persistence, UI, external services, IoC wiring).
CapabilityC-02

Dependency Rule Audit

Reviews code snippets, project references, or architectural descriptions and identifies every place where an inner layer depends on an outer layer — the primary violation Onion Architecture exists to prevent. Produces a prioritized list of violations with explanations.

Directly applies Palermo's governing constraint: 'all code can depend on layers more central, but code cannot depend on layers further out from the core.'
CapabilityC-03

Interface/Implementation Split

For any infrastructure concern (persistence, email, payment, file storage, external API), specifies exactly where the interface should be defined (inner layer) and where the implementation should live (outer layer), including naming conventions and the IoC wiring location.

Implements Palermo's application of the Dependency Inversion Principle: repository interfaces such as ICustomerRepository are defined in the Domain Services layer; SqlCustomerRepository implementing them lives in the Infrastructure layer.
CapabilityC-04

Project Scaffold Generator

Generates a complete folder structure, project/namespace layout, and starter files for a new application adhering to Onion Architecture — tailored to the described domain, use cases, and target ecosystem (C#, Java, Python, etc.).

Produces the four-project structure Palermo prescribes: Core (Domain Model + Domain Services), Application (Application Services + application interfaces), Infrastructure (persistence, external services), and UI/Tests at the outermost layer.
CapabilityC-05

Architecture Comparison

Provides precise, side-by-side analysis of Onion Architecture versus N-Tier Layered Architecture, Hexagonal Architecture (Ports & Adapters), and Robert Martin's Clean Architecture — including where they agree, where Palermo's framing differs, and which pattern fits a given context.

Anchored to Palermo's specific 2008 framing and his stated contrast with database-centric N-Tier design, while accurately characterizing the relationship to Alistair Cockburn's Hexagonal Architecture and Martin's Clean Architecture.
CapabilityC-06

Refactoring Roadmap

Given a description of an existing layered or database-centric application, produces an incremental refactoring sequence to migrate toward Onion Architecture — identifying which violations to fix first, which seams to extract interfaces at, and how to introduce the IoC container boundary.

Based on the extraction pattern Palermo describes: identify infrastructure concerns polluting inner layers, define interfaces where the inner layer needs capabilities, move implementations outward, wire at the application entry point.
Tested

Graded before it shipped.

Every skill is scored against independent scenarios for methodology fidelity before it goes live — not vibes, a rubric.

What it produces
OutputD-01

Onion Layer Map

A labeled diagram of your application's four concentric layers — Domain Model, Domain Services, Application Services, Infrastructure — with your specific classes, interfaces, and concerns placed correctly in each ring. Reveals where current code violates the dependency rule at a glance.

OutputD-02

Dependency Violation Report

A structured list of every inward-dependency violation found in your codebase: the offending class, what it depends on, which layer rule it breaks, and the recommended fix. Prioritized by architectural impact.

OutputD-03

Project Scaffold

A ready-to-use folder structure and project/namespace layout for your application, with starter files showing where Domain entities, repository interfaces, application services, and infrastructure implementations go — including IoC wiring at the entry point.

OutputD-04

Interface/Implementation Split Spec

A complete specification for each infrastructure concern in your application: the interface name, its location in the inner layer, the implementation class name, its location in the infrastructure layer, and the constructor injection or IoC registration pattern.

The source

Grounded in the original work.

Every answer traces back to a real source and the practitioner who wrote it — not a secondhand summary. Here is the source of record.

Source authorA-01

Jeffrey Palermo

Jeffrey Palermo is a software architect and Microsoft MVP who introduced Onion Architecture in a canonical 4-part blog series in 2008 on jeffreypalermo.com. He is a frequent conference speaker at events including NDC and CodeMash, and his architectural pattern is widely cited in Domain-Driven Design and Clean Architecture literature. Palermo has spent his career in .NET enterprise development and is a recognized authority on application architecture and software design principles.

Status · Inspired by Jeffrey Palermo’s work — not yet claimed. Are you Jeffrey Palermo?
Primary sourceS-01

Onion Architecture (4-part blog series, 2008)

by Jeffrey Palermo

Creator of Onion Architecture; Microsoft MVP; conference speaker at NDC and CodeMash; originator cited in DDD and Clean Architecture literature.

Read the original ↗
Citationjeffreypalermo.com
In the build queue

Be first to run it.

Onion Architecture is being built right now. Leave your email and we’ll tell you the moment it goes live.

Notify meEmail
At launchI have an existing C# application where my service classes directly call DbContext and my Domain project references our Data project. I want to audit which dependencies violate the Onion Architecture rules and get a prioritized refactoring plan.