Porque seu CRUD não é DDD (mesmo c/uma bela estrutura de pastas)
![]() |
Neste artigo vou comparar CRUD com DDD, e, o objetivo não é dizer que CRUD é “errado” mas mostrar o que você ganha e o que você perde em cada abordagem. |
Vamos definir um cenário igual para ambas as abordagens: Estamos tratando com Pedido, ItemPedido e Produto onde nosso objetivo será criar um pedido e adicionar um item com um preço do produto. Um cenário bem simples.
Regras
reais do negócio:
O preço do item deve ser o preço no momento da
compra
O pedido não pode mudar se o preço do produto mudar depois
O pedido
controla seus próprios itens
Você vai encontrar este exemplo de código em 90% dos exemplos na internet:
public class Pedido { public int Id { get; set; } public List<ItemPedido> Itens { get; set; } = new(); } public class ItemPedido { public int Id { get; set; } public int PedidoId { get; set; } public int ProdutoId { get; set; } public Produto Produto { get; set; } public int Quantidade { get; set; } } public class Produto { public int Id { get; set; } public string Nome { get; set; } public decimal Preco { get; set; } } |
Agora o caso de uso:
var pedido = db.Pedidos.Find(pedidoId); var produto = db.Produtos.Find(produtoId); pedido.Itens.Add(new ItemPedido { Produto = produto, Quantidade = 2 }); db.SaveChanges(); |
Problemas (reais) deste código:
❌ Regra 1 quebrada:
pedido.Itens[0].Produto.Preco
Se o preço do produto mudar amanhã
→ o pedido muda retroativamente
❌ Regra 2 inexistente
Pedido não
protege invariantes
Qualquer código pode alterar tudo
❌ Pedido não
controla nada
Ele é só um “saco de dados”
Toda regra está espalhada
Quando ESSE modelo é aceitável ?
✔ CRUD simples
✔ Sistemas
administrativos
✔ Baixo risco
✔ Histórico não crítico
Aqui usar DDD
é desnecessário, e isso não esta errado.
MODELO 2 — DDD DE VERDADE
Vamos definir um Modelo de domínio que não seja anêmico.
public sealed class Pedido { private readonly List<ItemPedido> _itens = new(); public IReadOnlyCollection<ItemPedido> Itens => _itens; public void AdicionarItem(ProdutoSnapshot produto, Quantidade quantidade) { _itens.Add(new ItemPedido(produto, quantidade)); } } public sealed class ItemPedido { public Guid ProdutoId { get; } public string NomeProduto { get; } public PrecoUnitario PrecoUnitario { get; } public Quantidade Quantidade { get; } internal ItemPedido(ProdutoSnapshot produto, Quantidade quantidade) { ProdutoId = produto.Id; NomeProduto = produto.Nome; PrecoUnitario = produto.Preco; Quantidade = quantidade; } } public sealed record ProdutoSnapshot( Guid Id, string Nome, PrecoUnitario Preco ); |
O Snapshot é uma cópia imutável do estado de algo no momento em que um fato de negócio acontece.
Um pedido não referencia o produto. Ele registra como o produto era quando foi comprado.
Caso de uso (Application Layer)
var produtoDto = catalogoApi.ObterProduto(produtoId); var snapshot = acl.Traduzir(produtoDto); var pedido = repo.ObterPorId(pedidoId); pedido.AdicionarItem(snapshot, new Quantidade(2)); repo.Salvar(pedido); |
Entendendo o código:
1- catalogoApi.ObterProduto(produtoId)
O que está
acontecendo aqui?
Você está saindo do Bounded Context de
Pedidos e consultando o Catálogo, que é um Upstream.
Esse método:
NÃO retorna uma entidade Produto
NÃO retorna algo do
domínio de Pedidos
Retorna Published Language do Catálogo
2 - acl.Traduzir(produtoDto)
Aqui está o coração do DDD
Essa linha é onde DDD realmente acontece.
A ACL responde à pergunta: “O que desse dado externo faz sentido
dentro do meu domínio?”
O que a ACL faz (e só ela pode fazer)
✔ Valida se o dado externo é aceitável
✔ Traduza nomes e conceitos
✔
Cria Value Objects
✔ Cria o Snapshot
✔ Remove tudo que não importa
O que
esse modelo garante:
✔ Regra 1 protegida
Preço é copiado
Histórico preservado
✔ Regra 2 protegida
Pedido controla seus itens
Ninguém altera estado direto
✔
Linguagem do negócio explícita
pedido.AdicionarItem(...)
Não :
pedido.Itens.Add(...)
Comparação direta (sem dó nem piedade)
| Aspecto | CRUD | DDD |
| Facilidade inicial | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| Curva de aprendizado | Baixa | Alta |
| Proteção de regras | ❌ | ✅ |
| Histórico consistente | ❌ | ✅ |
| Domínio expressivo | ❌ | ✅ |
| Escalabilidade conceitual | ❌ | ✅ |
| Custo inicial | Baixo | Alto |
| Custo a longo prazo | Alto | Baixo |
O ponto mais importante (que quase ninguém diz)
DDD não
substitui CRUD.
DDD substitui sistemas que quebram quando crescem.
Se o sistema:
não cresce
não muda
não tem regra
crítica
CRUD é a escolha correta.
Mas se:
ras egras evoluem
o histórico importa
múltiplos times mexem
o domínio é
estratégico
o CRUD cobra a conta depois.
CRUD otimiza o hoje.
DDD otimiza o amanhã.
E estamos
conversados...
"Todavia para nós há um só Deus, o Pai, de quem é
tudo e em quem estamos; e um só Senhor, Jesus Cristo, pelo qual são todas as
coisas, e nós por ele."
1 Coríntios 8:6
Referências:
NET - Unit of Work - Padrão Unidade de ...