Arquitetura Clean DDD: a mais próxima do mundo real

Se você ainda não ouviu falar de Clean DDD Architecture, deveria. Na minha opinião, esta é uma das melhores que já surgiram, porque ela consegue entregar ótimos resultados mesmo sendo muito simples. É o tipo de arquitetura com baixo custo de setup, mas com excelente separação de conceitos e de fácil manutenção/crescimento.

Um exemplo do mundo real

Minha empresa funciona dentro de um escritório de coworking. Obviamente, temos todo o know how necessário para operar o negócio, mas dependemos de uma infraestrutura que é fornecida pelo espaço (internet, sala de reunião, café, luz, água, limpeza do espaço, segurança, e por aí vai), e não estamos realmente interessados em como essa infraestrutura é obtida nem operada por eles. Só queremos usar!

Quando fechamos um novo projeto, convidamos o cliente para um primeiro call via Skype, onde o gestor apresenta a ele nosso processo de trabalho. Temos o checklist do que deve ser abordado nessa reunião (ou seja, a regra do negócio), mas precisamos da internet para viabilizar a ligação (infraestrutura). Um dos motivos para operar dentro de um coworking e não ter ído para espaço próprio é que não quero me preocupar com a infraestrutura (de onde vem a internet, quanto custa, como faz se der problema). Só preciso que funcione quando o gestor for realizar o call.

Esse exemplo representa as preocupações de uma arquitetura Clean DDD.

Transformando este exemplo em arquitetura

Considere as seguintes camadas:
- Domain (minha empresa)
- Infra (o coworking)
- Presentation (whatever)

Domain precisa da infra para funcionar, mas não quer nem saber como essa infra acontece. E não deveria mesmo: voltando ao mundo real, imagine se eu precisar alterar meu checklist de projeto só porque eu troquei de coworking!

Domain deve ser uma camada que não depende de mais nada. Nenhum plugin, pacote, e nem mesmo outros projetos da mesma Solution. Ela detém o negócio e é ela quem dita todas as regras. Domain usa persistência, gateway de pagamento, armazenamento de arquivos, etc., mas não tem nada a ver com a forma como nada disso acontece.

Falando de código

Considere este exemplo (simplificado para facilitar a explicação):

namespace Domain.Services
{
public class OrderService
{
private readonly IPaymentService _paymentService;
private readonly IRepository _repository;

public OrderService(
PaymentService paymentService,
IRepository repository)
{
_paymentService = paymentService;
_repository = repository;
}

public void ProcessOrder(int orderId)
{
var order = _repository.GetOrder(orderId);
var client = _repository.GetClient(order.ClientId);

if (client.GatewayId == null)
_paymentService.RegisterClient(client);

var paymentResult = _paymentService.Pay(order);

if (paymentResult.OK)
order.Status = OrderStatus.PaymentCompleted;

_repository.Commit();
}
}
}
// --------------------
namespace Infrastructure
{
public class PaymentService : Domain.Interfaces.IPaymentService
{
// Implementação aqui.
}

public class Repository : Domain.Interfaces.IRepository
{
// Implementação aqui.
}
}
// --------------------
namespace WebApp
{
public static class AppStart
{
public static void Configure(IoC container)
{
container.Register()
.From<Domain.Interfaces.IPaymentService>()
.To<Infrastructure.PaymentService>();

container.Register()
.From<Domain.Interfaces.IRepository>()
.To<Infrastructure.Repository>();
}
}
}

A grande sacada é: Domain é camada que, além de deter toda a regra de negócio, especifica quais são as interfaces (serviços externos) que precisa, e que serão implementados pela infraestrutura.

Com isso, Domain não referencia Infraestrutura nem qualquer outro projeto/plugin externo. Tudo isso fica preso na Infraestrutura. Domain cresce sem depender de ninguém, e se qualquer coisa mudar no projeto relacionada infraestrutura, não faz a menor diferença para Domain.

A implementação da infraestrutura é injetada em tempo de execução pela camada do projeto que realmente executa (no caso, WebApp).

Considerações finais

Para mim tem sido revelador pensar em código como se estivesse organizando/montando uma empresa. Se você tem uma empresa/startup ou atua com gestão de pessoas, sabe dos desafios de se estruturar bem um time e documentar os processos para que tudo funcione bem.

Tenho visto que as lições da gestão do mundo real se aplicam perfeitamente ao estruturar código, e o inverso também é válido na maior parte das vezes.

E é isso :)

Further Reading

Este é um dos melhores artigos que li sobre esse tipo de arquitetura. Ele aponta para um sample no GitHub que também recomendo muito! Tem ideias geniais lá, como extrair as queries dessa forma.