Clean Architecture in ASP.NET Core
Clean Architecture in ASP.NET Core
👤 Rohan Kumawat⏱️ 16 min read
What is Clean Architecture in ASP.NET Core?
Clean Architecture is a software design pattern focused on making your application independent of frameworks, UI, database, and external services. It improves maintainability, testability, and scalability by separating your project into well-defined layers. The Dependency Rule states that dependencies must always point inward toward the core business logic.
Core Principles
Clean Architecture ensures independence from frameworks, UI, and database implementations.
It allows your business logic to stay pure and reusable across multiple applications like Web,
Mobile, or Desktop. All dependencies must flow inward, making the core system stable and
long-lasting.
Clean Architecture Layer Structure
A typical Clean Architecture setup in ASP.NET Core contains four layers: Domain, Application,
Infrastructure, and WebAPI. Domain holds entities and core logic, Application stores business
rules and use cases (often using CQRS), Infrastructure handles EF Core database access and
external services, and WebAPI exposes the application through controllers and routes.
Folder Structure
Below is the recommended folder structure:
/src
/Domain
/Application
/Infrastructure
/WebAPI
Step 1: Create a New Solution
Create the main solution using .NET CLI.
dotnet new sln -n CleanArchitectureDemo
Step 2: Create Four Projects
Create the Domain, Application, Infrastructure, and WebAPI projects.
dotnet new classlib -n Domain
dotnet new classlib -n Application
dotnet new classlib -n Infrastructure
dotnet new webapi -n WebAPI
Step 3: Add Projects to Solution
After creating the folders, add them to your solution.
dotnet sln add Domain/Domain.csproj
dotnet sln add Application/Application.csproj
dotnet sln add Infrastructure/Infrastructure.csproj
dotnet sln add WebAPI/WebAPI.csproj
Step 4: Add Project References
Define how layers depend on each other according to Clean Architecture rules.
dotnet add Application/Application.csproj reference Domain/Domain.csproj
dotnet add Infrastructure/Infrastructure.csproj reference Application/Application.csproj
dotnet add WebAPI/WebAPI.csproj reference Application/Application.csproj
dotnet add WebAPI/WebAPI.csproj reference Infrastructure/Infrastructure.csproj
Domain Layer Example
A simple entity inside the Domain layer.
namespace Domain.Entities
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
}
}
Application Layer Example (CQRS Use Case)
This layer defines the application logic, interfaces, and use cases.
using Domain.Entities;
namespace Application.Products.Queries
{
public class GetProductsQuery { }
public interface IGetProductsQueryHandler
{
Task> HandleAsync();
}
}
Infrastructure Layer Example (EF Core Implementation)
Infrastructure contains real database logic and external service implementations.
using Domain.Entities;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Data
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }
public DbSet<Product> Products => Set<Product>();
}
}
using Application.Products.Queries;
using Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Handlers
{
public class GetProductsQueryHandler : IGetProductsQueryHandler
{
private readonly AppDbContext _context;
public GetProductsQueryHandler(AppDbContext context)
{
_context = context;
}
public async Task<List<Domain.Entities.Product>> HandleAsync()
{
return await _context.Products.ToListAsync();
}
}
}
WebAPI Layer Example (Controller)
The presentation layer exposes API endpoints for the application.
using Application.Products.Queries;
using Microsoft.AspNetCore.Mvc;
namespace WebAPI.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IGetProductsQueryHandler _handler;
public ProductsController(IGetProductsQueryHandler handler)
{
_handler = handler;
}
[HttpGet]
public async Task GetProducts()
{
var products = await _handler.HandleAsync();
return Ok(products);
}
}
}
Register Services in Program.cs
Configure dependency injection for Application and Infrastructure layers.
using Application.Products.Queries;
using Infrastructure.Data;
using Infrastructure.Handlers;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IGetProductsQueryHandler, GetProductsQueryHandler>();
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
Run the Application
Finally, run the API using:
dotnet run --project WebAPI/WebAPI.csproj