DEV Community

Nikhil Wagh
Nikhil Wagh

Posted on

Clean Architecture in .NET 10: Patterns That Actually Work in Production (2025 Guide)

Clean Architecture isn’t just a buzzword—it’s the difference between a codebase that scales with your business and one that turns into a tangled mess.

With .NET 10, we’ve got better tooling, records, DI, and minimal APIs. But clean architecture still depends on decisions you make—especially in the early design phase.

In this post, we’ll look at practical patterns and real-world folder structure that help Clean Architecture thrive in production, not just in theory.

What Is Clean Architecture (Quick Recap)?

Coined by Uncle Bob (Robert C. Martin), Clean Architecture separates concerns and enforces independence of frameworks, UI, and business rules.

Core Concepts:

  • Dependency Rule: Inner layers know nothing about outer layers
  • Use Cases drive application logic
  • Entities represent business objects
  • Interfaces define contracts, implemented by outer layers

Recommended Project Structure in .NET 10

/src
 ├── MyApp.Api                // Minimal API / MVC
 ├── MyApp.Application        // Use cases (CQRS, DTOs)
 ├── MyApp.Domain             // Business rules (Entities, Enums, Interfaces)
 ├── MyApp.Infrastructure     // EF Core, Services, External APIs
 └── MyApp.Tests              // Unit & Integration Tests

Enter fullscreen mode Exit fullscreen mode

Tip: Stick to single responsibility per project. You’ll thank yourself during maintenance.

Patterns That Work in Production

1. CQRS (Command Query Responsibility Segregation)

Split reads and writes into separate flows for clearer logic.

// Command
public record CreateOrderCommand(string CustomerId) : IRequest<Guid>;

// Handler
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, Guid>
{
    public async Task<Guid> Handle(CreateOrderCommand cmd, CancellationToken ct)
    {
        var order = new Order(cmd.CustomerId);
        _db.Orders.Add(order);
        await _db.SaveChangesAsync(ct);
        return order.Id;
    }
}
Enter fullscreen mode Exit fullscreen mode

Use MediatR or Pure DI to handle commands/queries cleanly.

2. Interfaces + Inversion of Control

Define contracts in Domain or Application, and implement them in Infrastructure.

// In Domain or Application
public interface IEmailSender
{
    Task SendAsync(string to, string subject, string body);
}

// In Infrastructure
public class SendGridEmailSender : IEmailSender
{
    public Task SendAsync(string to, string subject, string body) => ...
}
Enter fullscreen mode Exit fullscreen mode

3. Entity Encapsulation

Avoid anemic models. Keep domain logic inside the entity.

public class Order
{
    private readonly List<OrderItem> _items = new();
    public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();

    public void AddItem(Product product, int qty)
    {
        if (qty <= 0) throw new ArgumentException("Qty must be positive");
        _items.Add(new OrderItem(product, qty));
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Minimal API + Slim Controllers

In .NET 10, you can use Minimal APIs for cleaner endpoints, especially for microservices.

app.MapPost("/orders", async (CreateOrderCommand cmd, ISender mediator) =>
{
    var id = await mediator.Send(cmd);
    return Results.Created($"/orders/{id}", new { id });
});
Enter fullscreen mode Exit fullscreen mode

Or keep controllers lean and logic in handlers.

Testing Strateg

  • Domain Layer: Unit test without dependencies
  • Application Layer: Mock external services (email, payment)
  • Infrastructure: Integration tests (EF Core, APIs)

Use Testcontainers for real Postgres/SQL Server testing locally.

Pitfalls to Avoid

  • Don’t let UI reference Infrastructure directly
  • Don’t put logic in controllers
  • Avoid tight coupling between Use Cases and EF Core
  • Don’t abuse abstraction—only extract interfaces when needed

Tools That Help

  • MediatR – decoupled handlers
  • xUnit + FluentAssertions – clean tests
  • Mapster or AutoMapper – model mapping
  • Serilog – structured logging
  • EF Core 8+ – for Infrastructure layer

Summary

Clean Architecture in .NET 10 is not about fancy diagrams—it's about building apps that are testable, flexible, and understandable.

If you stick to core principles and apply modern tools mindfully, your app will scale for years to come.

What Patterns Have You Used?

Do you use Clean Architecture in your current project? What’s your biggest struggle or win? Drop it in the comments!

Top comments (0)