Index · How it works

1 min read
Mid-level3 min read
Rapid overview

How it works

Keep business rules independent of frameworks, UI, and data stores by forcing dependencies inward.

!alt text


Detailed Explanation

Layer Responsibilities

+-------------------------------+
|        Presentation           |  Controllers, endpoints
+-------------------------------+
|          Application          |  Use cases, CQRS
+-------------------------------+
|             Domain            |  Entities, invariants
+-------------------------------+
|        Infrastructure         |  DBs, brokers, APIs
+-------------------------------+

Dependency Flow

Presentation -> Application -> Domain
Infrastructure -> Application (via interfaces)

Example in a Trading Context

Domain layer (core rules):

public sealed class Order
{
    public Guid Id { get; } = Guid.NewGuid();
    public string Symbol { get; private set; }
    public decimal Quantity { get; private set; }

    private Order() { }

    public static Order Create(string symbol, decimal quantity)
    {
        if (string.IsNullOrWhiteSpace(symbol))
            throw new ArgumentException("Symbol is required", nameof(symbol));
        if (quantity <= 0)
            throw new ArgumentException("Quantity must be positive", nameof(quantity));

        return new Order { Symbol = symbol.ToUpperInvariant(), Quantity = quantity };
    }
}

Application layer (use case):

public sealed record PlaceOrderCommand(string Symbol, decimal Quantity) : IRequest<Guid>;

public sealed class PlaceOrderHandler : IRequestHandler<PlaceOrderCommand, Guid>
{
    private readonly IOrderRepository _orders;

    public PlaceOrderHandler(IOrderRepository orders)
    {
        _orders = orders;
    }

    public async Task<Guid> Handle(PlaceOrderCommand request, CancellationToken ct)
    {
        var order = Order.Create(request.Symbol, request.Quantity);
        await _orders.AddAsync(order, ct);
        return order.Id;
    }
}

Infrastructure layer (implementation):

public sealed class SqlOrderRepository : IOrderRepository
{
    private readonly TradingDbContext _db;

    public SqlOrderRepository(TradingDbContext db)
    {
        _db = db;
    }

    public async Task AddAsync(Order order, CancellationToken ct)
    {
        _db.Orders.Add(order);
        await _db.SaveChangesAsync(ct);
    }
}

Presentation layer (API):

[ApiController]
[Route("api/orders")]
public sealed class OrdersController : ControllerBase
{
    private readonly IMediator _mediator;

    public OrdersController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpPost]
    public async Task<IActionResult> Post([FromBody] PlaceOrderCommand command, CancellationToken ct)
    {
        var orderId = await _mediator.Send(command, ct);
        return Ok(new { orderId });
    }
}