Skip to main content
SEMastery
Architectureintermediate

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.

10 min readUpdated May 19, 2026

A building with the vault at the centre

Think about how a bank building is designed. Right at the centre is the vault, holding the most precious thing — the money and the core rules about it. Around the vault are the staff areas, where employees follow procedures to serve customers. Further out are the service desks and machines that connect to the outside world. And at the very outside is the public entrance, where customers walk in.

Notice the direction of importance. The outer areas depend on and protect the inner ones. The vault does not depend on the front door — but the front door's whole purpose is to safely reach the vault. You could change the entrance, repaint the lobby, or swap the ATMs, and the vault and its rules stay untouched.

Clean Architecture organises your code the same way. Your most precious thing — the business rules — sits safe in the centre, knowing nothing about databases, web frameworks, or screens. The outer layers depend inward toward it, never the reverse. Change your database or your web framework, and your core business logic does not even notice.

Let us learn the four layers, the one golden rule, and exactly which folders go where.

The four layers

Clean Architecture has four layers, arranged from the inside out. Each is usually a separate .NET project.

Figure 1: The four layers as rings. Dependencies point inward — outer layers know about inner ones, never the other way round.
  • Domain — the centre. Entities, value objects, aggregates, domain events, and the rules that are always true. It references nothing else.
  • Application — the use cases. It orchestrates the domain to do real tasks ("place an order", "register a user"), often using CQRS commands and queries. It depends only on the Domain.
  • Infrastructure — the outside-facing implementations: EF Core, the database, message brokers, email, file storage. It implements interfaces defined inside.
  • Presentation — the entry point, usually a Web API with controllers or minimal API endpoints. It receives requests and calls the Application.

The one golden rule: dependencies point inward

If you remember only one thing about Clean Architecture, remember this: source code dependencies only point inward.

The Dependency Rule

Presentation
Application
Domain
Infrastructure

Steps

1

Domain

Depends on NOTHING — pure business rules

2

Application

Depends only on Domain

3

Infrastructure

Depends on Domain + Application (implements their interfaces)

4

Presentation

Depends on Application; wires everything at startup

Each layer may depend only on the layers inside it. The Domain depends on nothing; the outer layers depend on the core.

This is what makes the architecture "clean." Your business rules (Domain) do not know whether you use SQL Server or PostgreSQL, ASP.NET Core or something else. Those are details on the outside. You can swap them without touching the core.

💡

A quick test: open your Domain project and look at its references. If it references EF Core, ASP.NET Core, or any database package — the rule is broken. The Domain should reference nothing but plain .NET.

The folder structure, layer by layer

Here is a typical solution. Each layer is a project, and inside each project the folders group related things.

MySolution/
├── src/
│   ├── MyApp.Domain/            // the core — references nothing
│   │   ├── Orders/
│   │   │   ├── Order.cs             // entity / aggregate root
│   │   │   ├── OrderItem.cs         // entity
│   │   │   ├── OrderStatus.cs       // value object / enum
│   │   │   └── IOrderRepository.cs  // interface (abstraction)
│   │   ├── Customers/
│   │   └── Common/                  // base classes, domain events
│   │
│   ├── MyApp.Application/        // use cases — references Domain
│   │   ├── Orders/
│   │   │   ├── PlaceOrder/
│   │   │   │   ├── PlaceOrderCommand.cs
│   │   │   │   └── PlaceOrderHandler.cs
│   │   │   └── GetOrder/
│   │   ├── Common/
│   │   │   └── IEmailService.cs     // interface the app needs
│   │   └── DependencyInjection.cs
│   │
│   ├── MyApp.Infrastructure/    // implementations — references Domain + Application
│   │   ├── Persistence/
│   │   │   ├── AppDbContext.cs
│   │   │   └── OrderRepository.cs   // implements IOrderRepository
│   │   ├── Services/
│   │   │   └── EmailService.cs      // implements IEmailService
│   │   └── DependencyInjection.cs
│   │
│   └── MyApp.Api/               // entry point — references Application (+ Infra at startup)
│       ├── Endpoints/
│       │   └── OrderEndpoints.cs
│       ├── Middleware/
│       └── Program.cs

Read the comments next to each project — they show exactly what each layer is allowed to reference. This is the dependency rule made real.

The clever trick: Dependency Inversion

Here is a puzzle. The Application needs to save orders to a database. But saving to a database is Infrastructure, which is an outer layer. If the Application depended on Infrastructure, the arrows would point outward — breaking the rule. How do we solve this?

The answer is the Dependency Inversion Principle. The inner layer defines an interface for what it needs, and the outer layer provides the implementation.

Figure 2: Dependency Inversion. The Application defines IOrderRepository; Infrastructure implements it. The dependency arrow still points inward.

In code:

// In Domain (or Application) — the inner layer says WHAT it needs
public interface IOrderRepository
{
    Task Add(Order order, CancellationToken ct);
    Task<Order?> GetById(int id, CancellationToken ct);
}
 
// In Infrastructure — the outer layer says HOW it is done
public class OrderRepository(AppDbContext db) : IOrderRepository
{
    public async Task Add(Order order, CancellationToken ct) =>
        await db.Orders.AddAsync(order, ct);
 
    public async Task<Order?> GetById(int id, CancellationToken ct) =>
        await db.Orders.FindAsync([id], ct);
}

The Application uses IOrderRepository and never knows EF Core exists. Infrastructure provides the real thing. The arrow points inward, and the rule holds.

Wiring it together at startup

The layers stay separate, but someone must connect them. That job belongs to the Presentation project at startup (the "composition root"). It is the only place allowed to know about every layer:

// Program.cs in MyApp.Api
builder.Services.AddApplication();      // from MyApp.Application
builder.Services.AddInfrastructure(builder.Configuration); // from MyApp.Infrastructure
 
var app = builder.Build();
app.MapOrderEndpoints();
app.Run();

Each layer exposes a small AddXxx() method that registers its own services. The API just calls them. This keeps each layer's wiring inside that layer, and the API stays clean.

A request's journey through the layers

When a request arrives, it travels inward through the layers and back out:

Figure 3: A request flows from the API into the Application, through the Domain rules, out to Infrastructure for saving, and back as a response.

Where does each thing go?

A common beginner question is "which folder does this belong in?" This table answers the most frequent ones:

ThingLayerWhy
Entity / aggregate (Order)DomainIt is core business data with rules
Value object (Money, Email)DomainPure, rule-carrying values
Repository interfaceDomain / ApplicationThe core says what it needs
Use case / handler (PlaceOrder)ApplicationOrchestrates the domain
DTO / request / responseApplicationShapes data for a use case
Repository implementationInfrastructureEF Core detail
DbContext, email senderInfrastructureExternal-facing detail
Controller / endpointPresentationThe entry point
Program.cs, DI wiringPresentationThe composition root

A simple rule decides most cases: if it is a business rule, it goes inward (Domain/Application); if it talks to the outside world (database, network, files), it goes outward (Infrastructure/Presentation).

Why this structure makes testing easy

One of the biggest payoffs of Clean Architecture is testability. Because the Domain and Application depend on nothing concrete, you can test all your business rules without a database, a web server, or any framework.

Testing Each Layer

Domain tests
Application tests
Infrastructure tests
End-to-end tests

Steps

1

Domain

Pure unit tests — no DB, instant, test the rules

2

Application

Test use cases with fake repositories (in-memory)

3

Infrastructure

Integration tests against a real test database

4

End-to-end

Full HTTP tests through the API for key flows

Inner layers test fast with no infrastructure. Only the outer layers need real databases or HTTP.

Because the core defines interfaces, your Application tests can pass a simple fake repository instead of a real database. The tests run in milliseconds, and they test business behaviour, not plumbing. This speed and focus is hard to get when business rules are tangled up with EF Core and controllers.

Clean Architecture vs simple layering

Simple N-layerClean Architecture
Dependency directionOften top-to-bottom (UI → DB)Always inward (toward Domain)
Business rules depend on DB?Often yesNo — fully independent
Swap the database easily?HardEasy
Best forSmall, simple appsComplex, long-lived business apps

The big difference is where the database sits. In old-style layering, the database is at the bottom and everything depends on it. In Clean Architecture, the database is an outer detail, and the business rules sit safely at the centre, depending on nothing.

When to use Clean Architecture

Clean Architecture is a great fit for:

  • Complex business apps with rich rules that will live and grow for years.
  • Teams that want clear boundaries and testable business logic.
  • Projects where the database or framework might change, and you want the core protected.

It can be overkill for:

  • Tiny apps, prototypes, or simple CRUD where the ceremony of four projects adds more friction than value. There, simple layering or vertical slices may be lighter.
ℹ️

Clean Architecture and Vertical Slice Architecture are not enemies. A popular combination keeps Clean Architecture's project layers and dependency rule, while organising the Application layer by feature slices. You get strong boundaries and feature-first code together.

Quick recap

  • Clean Architecture has four layers: Domain (core rules), Application (use cases), Infrastructure (database and services), and Presentation (Web API).
  • The golden rule: source code dependencies point inward. The Domain depends on nothing.
  • Dependency Inversion lets the inner layers define interfaces that outer layers implement, so the database stays an outer detail.
  • Each layer is usually its own project, so the compiler enforces the rule; the Presentation project wires everything at startup.
  • Use it for complex, long-lived apps; for tiny CRUD apps, lighter structures may serve better.

Keep your business rules in the vault at the centre, let the outer layers depend inward to protect them, and you get a codebase where the important logic stays clean, testable, and safe from every change happening on the outside. Start with the four projects, respect the one golden rule, and let each layer reference only the ones inside it. Once that habit is in place, your application can grow for years — swapping databases, frameworks, and UIs along the way — while the heart of your software stays calm and untouched at the centre.

References and further reading

Related Posts