Domain-Driven Design: o que é e quando usar
1. Introdução ao Domain-Driven Design (DDD)
Domain-Driven Design (DDD) é uma abordagem de desenvolvimento de software que coloca o domínio do negócio no centro de todas as decisões arquiteturais. Proposto por Eric Evans em seu livro homônimo de 2003, o DDD propõe que a complexidade essencial de um sistema não está na tecnologia, mas sim nas regras e processos do negócio que ele precisa suportar.
Diferente de arquiteturas tradicionais como MVC (Model-View-Controller), que organizam o código por camadas técnicas (controladores, modelos, visões), o DDD organiza o código em torno do modelo de domínio. Enquanto no MVC o "Model" frequentemente se torna um simples reflexo do banco de dados, no DDD o modelo de domínio é rico em comportamento e regras de negócio.
2. Pilares Conceituais do DDD
Domínio e Subdomínios: Todo sistema opera em um domínio principal (core domain), que é a razão de existir do software. Ao redor dele, existem subdomínios de suporte (apoiam o core domain) e genéricos (problemas comuns já resolvidos por soluções prontas, como autenticação).
Ubiquitous Language: É o vocabulário compartilhado entre especialistas de negócio e desenvolvedores. Se o negócio chama algo de "Pedido", o código deve ter uma classe Pedido, e não Order ou SolicitacaoCompra. Essa linguagem única elimina ruídos de comunicação.
Bounded Contexts: São fronteiras explícitas onde um modelo de domínio é válido. Um "Cliente" no contexto de Vendas pode ter atributos diferentes do "Cliente" no contexto de Cobrança. Cada bounded context possui seu próprio modelo e linguagem.
text
// Exemplo de dois bounded contexts com modelos diferentes para "Cliente"
// Contexto: Vendas
public class Cliente {
private ClienteId id;
private String nome;
private Endereco enderecoEntrega;
private List<Pedido> historicoPedidos;
public void adicionarPedido(Pedido pedido) { ... }
}
// Contexto: Cobrança
public class Cliente {
private ClienteId id;
private String nome;
private String documentoFiscal;
private List<Fatura> faturasAbertas;
private ScoreCredito score;
public boolean podeParcelar(double valor) { ... }
}
3. Blocos de Construção Táticos
Entidades e Value Objects: Entidades possuem identidade única e são mutáveis ao longo do tempo. Value Objects são imutáveis e definidos apenas por seus atributos. Um Preco é um Value Object; um Pedido é uma Entidade.
text
// Entidade: possui identidade (id)
public class Pedido {
private PedidoId id;
private List<ItemPedido> itens;
private StatusPedido status;
public void confirmar() {
if (itens.isEmpty())
throw new DominioException("Pedido sem itens não pode ser confirmado");
this.status = StatusPedido.CONFIRMADO;
}
}
// Value Object: imutável, sem identidade
public final class Preco {
private final BigDecimal valor;
private final String moeda;
public Preco(BigDecimal valor, String moeda) {
if (valor.compareTo(BigDecimal.ZERO) < 0)
throw new IllegalArgumentException("Preço não pode ser negativo");
this.valor = valor;
this.moeda = moeda;
}
public Preco somar(Preco outro) {
if (!this.moeda.equals(outro.moeda))
throw new IllegalArgumentException("Moedas diferentes");
return new Preco(this.valor.add(outro.valor), this.moeda);
}
}
Aggregates e Aggregate Roots: Um Aggregate é um cluster de objetos que devem ser tratados como uma unidade. O Aggregate Root é a única porta de entrada para modificar qualquer objeto dentro do aggregate, garantindo consistência transacional.
text
// Aggregate Root: Pedido
public class Pedido {
private PedidoId id;
private List<ItemPedido> itens; // parte do aggregate
public void adicionarItem(Produto produto, int quantidade) {
// Regra: não pode adicionar item em pedido já confirmado
if (status == StatusPedido.CONFIRMADO)
throw new DominioException("Pedido já confirmado");
itens.add(new ItemPedido(produto, quantidade));
}
}
Repositories e Factories: Repositories abstraem a persistência, permitindo que o domínio permaneça puro. Factories encapsulam a criação de objetos complexos.
4. Domain Events e Commands
Domain Events notificam que algo relevante ocorreu no domínio. Commands representam intenções explícitas de alterar o estado.
text
// Domain Event
public class PedidoConfirmado {
private PedidoId pedidoId;
private LocalDateTime dataConfirmacao;
private List<ItemPedido> itens;
}
// Command
public class ConfirmarPedidoCommand {
private PedidoId pedidoId;
private String usuarioResponsavel;
}
// Fluxo: Application Service orquestra
public class PedidoApplicationService {
private PedidoRepository repositorio;
private EventPublisher publisher;
public void handle(ConfirmarPedidoCommand cmd) {
Pedido pedido = repositorio.buscarPorId(cmd.pedidoId);
pedido.confirmar(); // Domain logic pura
repositorio.salvar(pedido);
publisher.publicar(new PedidoConfirmado(
pedido.getId(), LocalDateTime.now(), pedido.getItens()
));
}
}
5. Serviços de Domínio e Aplicação
Domain Services contêm lógica de negócio que não se encaixa naturalmente em uma Entidade ou Value Object. Application Services orquestram casos de uso, coordenando Domain Services, Repositories e eventos.
text
// Domain Service: lógica que envolve múltiplas entidades
public class CalculadoraFrete {
public Frete calcular(Pedido pedido, Endereco destino) {
double pesoTotal = pedido.getItens().stream()
.mapToDouble(i -> i.getProduto().getPeso() * i.getQuantidade())
.sum();
return new Frete(pesoTotal, destino.getRegiao());
}
}
// Application Service: orquestração
public class FinalizarPedidoService {
private PedidoRepository pedidoRepo;
private CalculadoraFrete calculadora;
public void executar(PedidoId id, Endereco destino) {
Pedido pedido = pedidoRepo.buscarPorId(id);
Frete frete = calculadora.calcular(pedido, destino);
pedido.atribuirFrete(frete);
pedidoRepo.salvar(pedido);
}
}
6. Quando Aplicar DDD na Prática
Cenários ideais para DDD incluem domínios com regras de negócio complexas e mutáveis, como sistemas financeiros, seguros, logística ou saúde. Equipes maduras com conhecimento do negócio e disposição para iteração contínua são essenciais.
Quando evitar DDD: Sistemas CRUD simples (cadastros básicos), protótipos rápidos, MVPs com prazo curto ou times pequenos sem acesso a especialistas de negócio. O custo de modelagem não compensa se o domínio for trivial.
Trade-offs: DDD exige investimento inicial maior em modelagem e comunicação, mas retorna em manutenibilidade e adaptabilidade a longo prazo. Para domínios estáveis e simples, o custo supera o benefício.
7. DDD e Arquiteturas Modernas
DDD se alinha naturalmente com Clean Architecture e Hexagonal Architecture, que também pregam independência do domínio em relação a frameworks e infraestrutura.
Em microsserviços, cada bounded context tipicamente se torna um serviço independente, com seu próprio banco de dados e modelo. A comunicação entre bounded contexts ocorre via Domain Events ou APIs.
A combinação com CQRS (Command Query Responsibility Segregation) e Event Sourcing é comum em sistemas que precisam de auditoria completa e escalabilidade separada entre leitura e escrita.
text
// Exemplo: Bounded Context como microsserviço
// Serviço: Pedidos (bounded context próprio)
// Serviço: Estoque (outro bounded context)
// Comunicação via Domain Event
// PedidoConfirmado -> EstoqueService.atualizarEstoque()
8. Passos para Começar com DDD
Event Storming é uma técnica colaborativa onde equipe técnica e de negócio mapeiam eventos do domínio em post-its. Domain Storytelling usa narrativas para descobrir regras implícitas.
Comece pequeno: identifique um subdomínio core, modele um aggregate, implemente com testes de domínio. O modelo deve evoluir conforme o conhecimento do negócio aumenta — DDD é iterativo.
Ferramentas como testes de unidade focados em regras de domínio (não em infraestrutura) e refatoração contínua são práticas essenciais para manter o modelo relevante.
Referências
- Domain-Driven Design: Tackling Complexity in the Heart of Software (Eric Evans) — Livro original que define os fundamentos do DDD, leitura obrigatória para qualquer arquiteto de software
- Martin Fowler - BoundedContext — Artigo técnico que explica o conceito de Bounded Contexts com exemplos práticos
- Microsoft - Domain-Driven Design in Microservices — Guia oficial da Microsoft sobre implementação de DDD em arquitetura de microsserviços
- Alberto Brandolini - Event Storming — Site oficial da técnica de modelagem colaborativa Event Storming, essencial para iniciar projetos DDD
- Vaughn Vernon - Implementing Domain-Driven Design — Livro e recursos complementares sobre implementação prática dos padrões táticos do DDD
- DDD Community - DDD Weekly — Curadoria semanal de artigos, vídeos e discussões sobre Domain-Driven Design na comunidade global
- Clean Architecture vs DDD (Robert C. Martin) — Artigo que relaciona os princípios da Clean Architecture com os conceitos de DDD