Implementing Clean Architecture with .NET 5

Robert C. Martin’s “Clean Architecture” (or Onion/Hexagonal Architecture) is the gold standard for testable, maintainable enterprise systems. In this article, I will demonstrate how to structure a .NET 5 solution to enforce the Dependency Rule strictly.

The Dependency Rule

Source code dependencies must point only inward, toward high-level policies. The Inner circles (Domain) must not know anything about the outer circles (Database, Web, UI).

block-beta
    columns 1
    block:Layers
        block:Infra
            WebUI
            Infrastructure
        end
        block:Core
            Application
            Domain
        end
    end
    
    WebUI --> Application
    Infrastructure --> Application
    Application --> Domain
    Infrastructure --> Domain

    style Domain fill:#C8E6C9,stroke:#2E7D32
    style Application fill:#E8F5E9,stroke:#43A047
    style Infrastructure fill:#FFECB3,stroke:#FF6F00
    style WebUI fill:#E1F5FE,stroke:#0277BD

Solution Structure

Create your solution with these 4 specific projects:

  • Domain (Class Library): Enums, Value Objects, Domain Entities, Domain Exceptions. Dependencies: None.
  • Application (Class Library): Interfaces (`IRepository`), DTOs, MediatR Handlers, Validators. Dependencies: Domain.
  • Infrastructure (Class Library): EF Core DbContext, Email Service Implementation, Third Party API Clients. Dependencies: Application, Domain.
  • WebUI (Web API): Controllers, Middlewares. Dependencies: Application, Infrastructure.

Dependency Injection Glue

The trick is how `WebUI` wires everything up without breaking the abstraction. We use Extension methods in each layer.

// Infrastructure/DependencyInjection.cs
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration config)
{
    // Implementation details (EF Core) are hidden here
    services.AddDbContext<AppDbContext>(opt => 
        opt.UseSqlServer(config.GetConnectionString("Db")));
        
    services.AddScoped<IOrderRepository, OrderRepository>();
    return services;
}

This keeps `Startup.cs` clean: `services.AddApplication().AddInfrastructure(Configuration);`.


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.