.NET Core  

Implementing Clean Architecture in a .NET 10 Solution Guide

Introduction

Clean Architecture is a design approach that helps you build scalable, maintainable, and testable applications. It separates your code into clear layers so that business logic stays independent from frameworks, databases, and UI.

In modern .NET 10 applications, Clean Architecture is widely used for building enterprise-level APIs, microservices, and modular systems. In this article, you will learn how to implement Clean Architecture step by step using a layered folder structure with practical examples in simple, easy-to-understand language.

What is Clean Architecture?

Clean Architecture is a software design principle introduced to keep your core business logic independent of external concerns like databases, UI, and frameworks.

The main idea is:

  • Inner layers should not depend on outer layers

  • Outer layers can depend on inner layers

This ensures your application remains flexible and easy to change.

Core Principles of Clean Architecture

1. Separation of Concerns

Each layer has a specific responsibility.

  • Business logic → Core layer

  • Data access → Infrastructure layer

  • API/UI → Presentation layer

2. Dependency Rule

Dependencies always point inward.

  • Infrastructure depends on Application

  • Application depends on Domain

3. Testability

You can test business logic without database or UI.

4. Maintainability

Changes in one layer do not affect others.

Clean Architecture Layers in .NET 10

A typical Clean Architecture solution contains the following layers:

1. Domain Layer (Core)

This is the heart of your application.

Contains:

  • Entities

  • Value Objects

  • Domain Interfaces

  • Business Rules

Example:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

2. Application Layer

This layer contains business use cases.

Contains:

  • Interfaces (Repositories, Services)

  • DTOs

  • Business logic

Example:

public interface IProductRepository
{
    Task<Product> GetByIdAsync(int id);
}

3. Infrastructure Layer

This layer handles external concerns.

Contains:

  • Database (EF Core)

  • File systems

  • External APIs

Example:

public class ProductRepository : IProductRepository
{
    private readonly AppDbContext _context;

    public ProductRepository(AppDbContext context)
    {
        _context = context;
    }

    public async Task<Product> GetByIdAsync(int id)
    {
        return await _context.Products.FindAsync(id);
    }
}

4. Presentation Layer (API)

This is the entry point of your application.

Contains:

  • Controllers or Minimal APIs

  • Request/Response handling

Example:

app.MapGet("/products/{id}", async (int id, IProductRepository repo) =>
{
    var product = await repo.GetByIdAsync(id);
    return product is not null ? Results.Ok(product) : Results.NotFound();
});

Recommended Folder Structure in .NET 10

Here is a clean and scalable folder structure:

Solution
│
├── Domain
│   ├── Entities
│   ├── ValueObjects
│   └── Interfaces
│
├── Application
│   ├── Interfaces
│   ├── Services
│   └── DTOs
│
├── Infrastructure
│   ├── Data
│   ├── Repositories
│   └── Services
│
├── Presentation (API)
│   ├── Endpoints
│   └── Middleware
│
└── Shared (Optional)

Step-by-Step Implementation in .NET 10

Step 1: Create Solution

dotnet new sln -n CleanArchitectureDemo

Step 2: Create Projects

dotnet new classlib -n Domain
dotnet new classlib -n Application
dotnet new classlib -n Infrastructure
dotnet new webapi -n Presentation

Step 3: Add Project References

  • Application → Domain

  • Infrastructure → Application

  • Presentation → Application + Infrastructure

Step 4: Add Dependency Injection

builder.Services.AddScoped<IProductRepository, ProductRepository>();

Step 5: Configure Database

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(connectionString));

Step 6: Implement Use Case

public class GetProductService
{
    private readonly IProductRepository _repo;

    public GetProductService(IProductRepository repo)
    {
        _repo = repo;
    }

    public async Task<Product> Execute(int id)
    {
        return await _repo.GetByIdAsync(id);
    }
}

Step 7: Use in API

app.MapGet("/products/{id}", async (int id, GetProductService service) =>
{
    var result = await service.Execute(id);
    return Results.Ok(result);
});

Benefits of Clean Architecture in .NET 10

  • Scalable and modular design

  • Easy to test and maintain

  • Independent of frameworks

  • Better code organization

Common Mistakes to Avoid

  • Mixing business logic in controllers

  • Direct database access in API layer

  • Ignoring interfaces

  • Not following dependency rule

Real-World Example

In an e-commerce system:

  • Domain handles product rules

  • Application manages use cases

  • Infrastructure connects to database

  • API exposes endpoints

This structure allows easy scaling and feature additions.

Conclusion

Implementing Clean Architecture in .NET 10 helps you build modern, scalable, and maintainable applications. By separating concerns and following proper layering, you can create systems that are easy to test, extend, and manage.

Start with a simple structure, follow best practices, and gradually improve your architecture as your application grows.