Skip to main content
SEMastery
Architecturebeginner

Clean Architecture: The Missing Chapter Most Tutorials Skip

The missing chapter of Clean Architecture in .NET: where code really goes, what the dependency rule means, and the pragmatic choices tutorials skip.

11 min readUpdated October 10, 2025

A kitchen where the recipe never changes

Picture a busy restaurant kitchen. Right at the heart of it is the head chef's recipe book. It holds the true rules: how a biryani is layered, how much salt, what makes the dish correct. That book does not care which stove you own, which supplier sold the rice, or whether the order came from a waiter, a phone call, or an online app.

Around that book, cooks follow steps to actually make the dish. Around them, suppliers and machines bring ingredients and power. And at the very edge, waiters take orders from customers.

Now here is the important part. The recipe does not depend on the waiter. The waiter depends on the recipe. You can hire new waiters, buy a new stove, or switch your rice supplier, and the recipe stays exactly the same. The dish still tastes right.

Clean Architecture organises code the same way. Your business rules are the recipe book, kept safe in the centre. Databases, web frameworks, and screens are the stoves and waiters on the outside. They depend inward, toward the rules. The rules depend on nothing.

Most tutorials show you this picture and four folders, then stop. That is where they lose you. This post is the missing chapter: the part about why those folders exist, where each piece of code truly belongs, and when it is wise to bend the rules.

What every tutorial shows you

Here is the diagram you have seen a hundred times. Four layers as rings, dependencies pointing inward.

Figure 1: The familiar four layers. Outer rings know about inner rings, never the reverse.
  • Domain is the centre. Entities, value objects, and rules that are always true. It references nothing.
  • Application holds the use cases: "place an order", "register a user". It depends only on Domain.
  • Infrastructure holds the outside parts: EF Core, the database, email, file storage.
  • Presentation is the entry point, usually a Web API.

This is correct, but it is only the cover of the book. Knowing the four names does not tell you where to put a new piece of code on a Tuesday afternoon. That is the chapter we are going to read now.

Chapter 1: The dependency rule is about change, not folders

People memorise "dependencies point inward" as a rule about project references. That is the how. The why is more useful: the rule decides what can break what.

When project B references project A, a change in A can force a change in B. So the arrow really means "this side is allowed to be hurt by changes on the other side". By pointing every arrow toward the Domain, you are saying: the outside is allowed to be disturbed by the core, but the core is never disturbed by the outside.

That is the real prize. Swap SQL Server for PostgreSQL, and your business rules do not change. Replace controllers with minimal APIs, and your rules do not change. The recipe book survives every renovation of the kitchen.

What a dependency arrow really means

Presentation
Application
Domain

Steps

1

Presentation

Can break when use cases change

2

Application

Can break when rules change

3

Domain

Breaks for no one

Read each arrow as 'is allowed to be broken by changes in'.

In .NET, the compiler enforces this for free. If your Domain project has no reference to Infrastructure, you simply cannot type using MyApp.Infrastructure; in a domain file. It will not compile. The build itself becomes the guard at the vault door.

Chapter 2: Where does each piece of code actually go?

This is the question tutorials dodge. Here is a simple table you can keep beside you.

Code you are writingWhere it goesWhy
Order, Money, business ruleDomainTruth that is always true
PlaceOrderHandler (a use case)ApplicationOrchestrates the domain
IOrderRepository (interface)ApplicationThe use case needs it
EF Core OrderRepository (class)InfrastructureThe how, swappable
OrdersController / endpointPresentationTakes the HTTP request
SendGridEmailSenderInfrastructureTalks to the outside world

Notice rows three and four. The interface lives inside, with the code that depends on it. The class that implements it lives outside. This split is the secret that makes the inward arrows possible, and it deserves its own chapter.

Chapter 3: Interfaces live inside, implementations live outside

Imagine the head chef writes a note: "I need someone to fetch rice." That note is an interface. The chef does not say which shop, which truck, or which person. The note stays in the recipe book.

Out in the world, an actual supplier reads the note and fulfils it. That supplier is the implementation. If one supplier closes, you hand the same note to another. The note never changes.

In code, the Application layer writes the note:

// Application layer — the "note". This is WHAT we need.
namespace MyApp.Application.Abstractions;
 
public interface IOrderRepository
{
    Task<Order?> GetByIdAsync(Guid id, CancellationToken ct);
    Task AddAsync(Order order, CancellationToken ct);
}

The Infrastructure layer fulfils the note:

// Infrastructure layer — the "supplier". This is HOW it works.
using MyApp.Application.Abstractions;
using Microsoft.EntityFrameworkCore;
 
namespace MyApp.Infrastructure.Persistence;
 
public sealed class OrderRepository : IOrderRepository
{
    private readonly AppDbContext _db;
 
    public OrderRepository(AppDbContext db) => _db = db;
 
    public Task<Order?> GetByIdAsync(Guid id, CancellationToken ct) =>
        _db.Orders.FirstOrDefaultAsync(o => o.Id == id, ct);
 
    public async Task AddAsync(Order order, CancellationToken ct)
    {
        await _db.Orders.AddAsync(order, ct);
    }
}

The use case only ever sees the interface:

// Application layer — the use case. It knows the note, not the supplier.
namespace MyApp.Application.Orders;
 
public sealed class PlaceOrderHandler
{
    private readonly IOrderRepository _orders;
 
    public PlaceOrderHandler(IOrderRepository orders) => _orders = orders;
 
    public async Task<Guid> Handle(PlaceOrder command, CancellationToken ct)
    {
        var order = Order.Create(command.CustomerId, command.Items);
        await _orders.AddAsync(order, ct);
        return order.Id;
    }
}

This is the Dependency Inversion Principle. The arrow that would normally point from Application to the database now points the other way, because the database code depends on an interface the Application owns. You have flipped the dependency. That flip is what lets every arrow point inward toward the core.

Figure 2: The flip. Infrastructure depends on the Application's interface, so the data arrow points inward.

The wiring that connects the note to the supplier happens at startup, in the Presentation layer, with one line:

builder.Services.AddScoped<IOrderRepository, OrderRepository>();

The core never sees that line. It only ever asked for the note.

Chapter 4: Make your architecture scream

Open a typical project and the top folders are Controllers, Services, Models, Repositories. That tells you the project is built with a web framework. It does not tell you what the app does.

The architect Robert C. Martin called this idea Screaming Architecture. The structure of your code should shout its purpose, the way a blueprint shouts "this is a hospital" and not "this is made of bricks". When someone opens your Application layer, they should see Orders, Payments, Shipping, not Handlers, Validators, Dtos.

Folders that scream the frameworkFolders that scream the app
ControllersOrders
ServicesPayments
RepositoriesCustomers
ModelsShipping

Group by feature first, then by type inside if you must. A new teammate should be able to guess what the system does from the folder names alone, before reading a single line of code.

Reading a project at a glance

Open repo
Read top folders
Understand purpose

Steps

1

Open repo

First impression matters

2

Read top folders

Orders, Payments, Shipping

3

Understand purpose

It is a shop, instantly clear

Good structure answers 'what does this do?' before 'how is it built?'.

Chapter 5: A request's full journey

It helps to watch one request travel through all the layers and back. Here is what happens when a customer places an order.

Figure 3: One request flowing inward to the core and back out, crossing each layer once.

Notice how the request enters from the outside, moves inward to touch the Domain rules, then comes back out. The Domain did the most important work, creating a valid order, yet it never knew there was a database or an HTTP request involved. It only knew the recipe.

Chapter 6: The chapter on breaking the rules

Here is the most honest part, and the part most guides leave out entirely: you are allowed to bend Clean Architecture, and sometimes you should.

Clean Architecture is a set of trade-offs, not a law. It buys you flexibility and testable rules. It costs you extra projects, extra interfaces, and more files to open. You should pay that cost only when you get something back.

  • Tiny apps. A weekend tool or a simple CRUD admin screen rarely needs four projects. Start with folders inside one project. Split later, when real pain shows up.
  • Anaemic domains. If your "entities" are just bags of properties with no rules, the Domain layer is empty ceremony. Consider a simpler style first.
  • One-to-one mapping passthroughs. When a use case just reads a row and returns it unchanged, the layers add typing with no thinking. That is fine to keep simple.

A popular middle path is Vertical Slice Architecture, where each feature is a self-contained slice instead of code spread across four horizontal layers. Many teams mix the two: Clean Architecture's dependency rule for the core, vertical slices for the features.

A quick word on tools. You will see many Clean Architecture tutorials reach for MediatR and MassTransit. As of 2025, both moved to a commercial license for larger companies. They are still good libraries, but they are not required. Clean Architecture is about which way the arrows point, not any NuGet package. Plenty of teams now use plain handler classes, a tiny hand-written dispatcher, or a free alternative, and lose nothing of the architecture.

The skill is not memorising the layers. The skill is knowing what each layer buys you, and refusing to pay when the purchase is not worth it.

Chapter 7: How to test that the rules hold

A nice bonus: because the rules live as project references, you can write a test that fails the build if someone breaks them. Libraries like NetArchTest let you assert your architecture in code.

[Fact]
public void Domain_should_not_depend_on_infrastructure()
{
    var result = Types.InAssembly(typeof(Order).Assembly)
        .Should()
        .NotHaveDependencyOn("MyApp.Infrastructure")
        .GetResult();
 
    Assert.True(result.IsSuccessful);
}

Now the recipe book is guarded not just by the compiler, but by a test that runs on every push. The architecture defends itself.

Putting it all together

Figure 4: The full picture. Interfaces inside, implementations outside, every arrow pointing toward the core.

The four folders were never the point. The point is a single, calm promise: the things that matter most, your business rules, are protected from the things that change most, your tools and frameworks. The recipe book outlives every stove.

Quick recap

  • Clean Architecture keeps your business rules safe in the centre, away from databases and frameworks.
  • The dependency rule means arrows point inward. The real meaning: the core is never broken by changes on the outside.
  • Interfaces go inside (with the code that needs them); implementations go outside in Infrastructure. This flip is what makes inward arrows possible.
  • Make your architecture scream the app's purpose by grouping folders by feature, not by framework type.
  • One request flows inward to the Domain and back out, and the core never learns about HTTP or SQL.
  • Bend the rules on purpose for tiny or anaemic apps. Clean Architecture is a trade-off, not a law.
  • You do not need MediatR or MassTransit (now commercially licensed) to do Clean Architecture.
  • Guard the rules automatically with an architecture test so the build defends your design.

References and further reading

Related Posts

Clean Architecture Folder Structure in .NET: A Simple Guide

Learn how to organise a .NET solution with Clean Architecture. Understand the Domain, Application, Infrastructure, and Presentation layers, the dependency rule, and the exact folders, with diagrams and examples.

Read more

Vertical Slice Architecture in .NET: The Easy Guide

Learn Vertical Slice Architecture in .NET in simple words. Organise code by feature instead of by layer, with diagrams, real examples, a comparison with Clean Architecture, and when to use each.

Read more

What Is a Modular Monolith? A Beginner-Friendly Guide for .NET

Understand the modular monolith in simple words: one app, strong internal walls. Learn how it compares to monoliths and microservices, why it is the 2026 default for most .NET teams, and how to build one.

Read more

Clean Architecture in .NET: The Benefits of Structured Software Design

A beginner-friendly guide to Clean Architecture in .NET. Learn the four layers, the dependency rule, and why structured software design keeps your code easy to change.

Read more

Why Clean Architecture Is Great for Complex .NET Projects

A friendly guide to why Clean Architecture shines on big, complex .NET projects: testable business rules, swappable infrastructure, and code that stays kind to change.

Read more

Screaming Architecture in .NET: Let Your Folders Tell the Story

Learn Screaming Architecture in .NET in plain words. Make your folder structure shout the business purpose, not the framework, with diagrams and real examples.

Read more