Arquitetura de Camadas e Dependências
Este documento define as dependências permitidas entre camadas (Cliente, Application, Domain e Infra), estabelecendo limites claros de acoplamento e pontos de integração para manter o desenho DDD consistente.
O que é permitido
- Controllers chamam apenas casos de uso (services) da camada de
Application. Applicationorquestra o fluxo e depende de contratos (interfaces) doDomain.Infraimplementa detalhes técnicos (ex.: Entity Framework) para os contratos definidos noDomain.
O que é proibido
Controllerschamarem repositórios diretamente.Services(Application) usaremDbContextdiretamente.
Exemplo correto
// API: Controller -> Application Service
[ApiController]
[Route("api/orders")]
public sealed class OrdersController : ControllerBase
{
private readonly IPlaceOrder placeOrder;
public OrdersController(IPlaceOrder placeOrder) => this.placeOrder = placeOrder;
[HttpPost]
public async Task<IActionResult> Post(PlaceOrderRequest request, CancellationToken ct)
{
await placeOrder.ExecuteAsync(request.ToCommand(), ct);
return Accepted();
}
}
// Application: caso de uso orquestra a persistência via repositório
public interface IPlaceOrder
{
Task ExecuteAsync(PlaceOrderCommand command, CancellationToken ct);
}
public sealed class PlaceOrderService : IPlaceOrder
{
private readonly IOrderRepository orderRepository;
private readonly IUnitOfWork unitOfWork;
public PlaceOrderService(IOrderRepository orderRepository, IUnitOfWork unitOfWork)
{
this.orderRepository = orderRepository;
this.unitOfWork = unitOfWork;
}
public async Task ExecuteAsync(PlaceOrderCommand command, CancellationToken ct)
{
var order = Order.Create(command.CustomerId, command.Lines);
await orderRepository.AddAsync(order, ct);
await unitOfWork.CommitAsync(ct);
}
}
// Domain: contrato de repositório
public interface IOrderRepository
{
Task AddAsync(Order order, CancellationToken ct);
}
// Infra: implementação com EF Core
public sealed class EfOrderRepository : IOrderRepository
{
private readonly AppDbContext db;
public EfOrderRepository(AppDbContext db) => this.db = db;
public Task AddAsync(Order order, CancellationToken ct)
=> db.Orders.AddAsync(order, ct).AsTask();
}
Exemplo incorreto (evitar)
// Service usando DbContext diretamente — proibido
public sealed class BadService
{
private readonly AppDbContext db;
public BadService(AppDbContext db) => this.db = db;
public Task Foo() => db.Orders.ToListAsync(); // viola a regra
}
// Controller invocando repositório — proibido
public sealed class BadController : ControllerBase
{
private readonly IOrderRepository repo;
public BadController(IOrderRepository repo) => repo = repo;
public async Task<IActionResult> Bad()
{
await repo.AddAsync(new Order(), default); // viola a regra
return Ok();
}
}