Saltar al contenido principal

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.
  • Application orquestra o fluxo e depende de contratos (interfaces) do Domain.
  • Infra implementa detalhes técnicos (ex.: Entity Framework) para os contratos definidos no Domain.

O que é proibido

  • Controllers chamarem repositórios diretamente.
  • Services (Application) usarem DbContext diretamente.

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();
}
}