Index · How it works
1 min readRapid overview
How it works
Keep business rules independent of frameworks, UI, and data stores by forcing dependencies inward.
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 });
}
}