// Architecture Styles · 02
Clean Architecture
Business rules in the middle; frameworks and databases plug in.
How it fits together
- Api uses / calls PlaceOrderHandler (sends command)
- Api uses / calls SqlOrderRepository (registers in DI)
- PlaceOrderHandler uses / calls OrderRepository (saves via)
- SqlOrderRepository implements OrderRepository
- PlaceOrderHandler uses / calls Order (runs rules)
- OrderRepository uses / calls Order
Spot it
- Unit tests need a real database
- Upgrading the ORM breaks business code
- Domain classes carry ORM attributes
It costs
More projects, interfaces and mapping code for every feature.
In code
// Applicationpublic interface IOrderRepository{ Task SaveAsync(Order order, CancellationToken ct);}public sealed class PlaceOrderHandler(IOrderRepository orders){ public async Task<Guid> HandleAsync(PlaceOrder command, CancellationToken ct) { var order = Order.Place(command.CustomerId, command.Lines); await orders.SaveAsync(order, ct); return order.Id; }}// Infrastructurepublic sealed class SqlOrderRepository(ShopDbContext db) : IOrderRepository{ public async Task SaveAsync(Order order, CancellationToken ct) { db.Orders.Add(order); await db.SaveChangesAsync(ct); }}// Api, composition rootbuilder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();Go deeperThe full explanation and what to ask for · 1 min readExpandCollapse
One rule: source dependencies point inward. Entities and use cases know nothing about HTTP, the ORM or queues; the outer rings implement interfaces the inner rings define. Hexagonal (ports and adapters) and onion architecture are the same idea drawn differently.
It pays off when the domain is complex and long-lived, or when infrastructure is likely to change. For a small CRUD service it mostly adds ceremony: interfaces with a single implementation and DTOs copied between layers.
It is layered architecture with the bottom dependency inverted through dependency inversion. It shapes the code inside one deployable; modular monolith and microservices decide how to cut the system into parts.
Ask for it
- dependency rule
- domain layer
- application layer / use cases
- ports and adapters
- infrastructure layer
- composition root
Restructure this solution into Domain, Application, Infrastructure and Api projects. Domain references nothing; Application defines ports such as an order repository interface; Infrastructure implements them with the ORM; wire it all up in the composition root.