Clean Architecture

Organizes code into concentric layers where the domain is the center and nothing from the outside touches it.

Context

Formulated by Robert C. Martin (Uncle Bob) in 2012 as a synthesis of Hexagonal, Onion and similar proposals. You want to isolate business rules from technical details (framework, DB, UI) so that you can change them without rewriting the domain.

Problem

When the domain depends on EF, ASP.NET, or a specific broker, technical changes break business logic.

Solution

Concentric layers following the Dependency Rule: dependencies point inward, never outward.

┌──────────────────────────────────────┐
│  Frameworks & Drivers (Web, EF, MQ)  │
│  ┌──────────────────────────────┐    │
│  │ Interface Adapters (DTOs,    │    │
│  │ Controllers, Presenters)     │    │
│  │  ┌──────────────────────┐    │    │
│  │  │ Use Cases (App)       │   │    │
│  │  │  ┌──────────────┐    │    │    │
│  │  │  │  Entities    │    │    │    │
│  │  │  └──────────────┘    │    │    │
│  │  └──────────────────────┘    │    │
│  └──────────────────────────────┘    │
└──────────────────────────────────────┘

#The four layers

  1. Entities: enterprise-wide business rules.
  2. Use Cases (interactors): application-specific rules; they orchestrate the entities.
  3. Interface Adapters: controllers, presenters, gateways. They convert data between the use cases' format and the outside's.
  4. Frameworks & Drivers: web, database, UI. The glue and the details.

#What sets it apart from Hexagonal and Onion

  • It is the most prescriptive: it names the layers, defines use cases as the central unit and says how data crosses boundaries (simple structures, not entities or database rows).
  • Hexagonal only says inside/outside; Onion puts the domain model at the center; Clean puts use cases in the lead.
  • In practice, all three share the dependency rule. If your team already applies one of them well, switching to another almost never justifies the effort. See the comparison.

#Rules

  1. Pure domain: no using Microsoft.EntityFrameworkCore, no web attributes, no DateTime.Now.
  2. Use Cases orchestrate; they do not contain domain rules.
  3. Output interfaces (repositories, services) live in the Application layer; external layers implement them (DI inversion).

#Typical Structure

CSHARP
src/
├── Domain/           ← Entities, VOs, events, rules. ZERO dependencies.
├── Application/      ← Use cases, ports (IRepository, IClock). Depends only on Domain.
├── Infrastructure/   ← EF, HTTP, MQ. Implements Application's ports.
└── Web/              ← ASP.NET. Composes the DI graph.
// Application defines the "port"
public interface IOrderRepository { Task<Order?> GetAsync(Guid id); }

// Infrastructure implements it
public class EfOrderRepository : IOrderRepository { /* ... */ }

// Web registers it
services.AddScoped<IOrderRepository, EfOrderRepository>();
When NOT to use it
  • Small microservices or scripts: the ceremony kills ROI.
  • Teams without the discipline to maintain correct dependencies.
Tradeoffs
Pro Con
Testable domain without infrastructure More projects and boilerplate
Change EF for Dapper without touching domain Extra mappings between layers
Fits perfectly with DDD Learning curve

#architecture #clean #ddd