DDD não é o que você esta pensando !!!
![]() |
Neste artigo veremos porque muita gente acha que esta usando DDD só porque criou camadas e organizou o código em pastas com nomes como Domain, Application, Infrastructure e API. |
Para não tornar o artigo longo e chato e vou assumir um cenário bem ingênuo mas que vai servir ao nosso propósito. Imagine que você tem a seguinte definição de classes em um sistema qualquer onde tem que tratar com Produto e Categoria:
Você vai encontrar este exemplo de código em 90% dos exemplos na internet definindo o relacionamento entre Produto e Categoria :
public class Categoria { public int Id { get; set; } public string Nome { get; set; } public virtual ICollection<Produto> Produtos { get; set; } } public class Produto { public int Id { get; set; } public string Nome { get; set; } public decimal Preco { get; set; } public int Estoque { get; set; } public int CategoriaId { get; set; } public virtual Categoria Categoria { get; set; } } |
No sistema foi adotada uma arquitetura em camadas com organização em pastas como : Domain, Application, Infrastructure e API. Até aqui tudo bem. O problema é você afirmar que esta usando a abordagem DDD neste sistema se baseando nisso.
Vamos por partes...
Resposta curta e honesta:
❌ Não. Esse modelo NÃO está correto sob a perspectiva de DDD.
✔ Ele está
correto sob a perspectiva de ORM / EF Core.
E isso é uma diferença
crítica.
Mas porque não é DDD ?
Vamos analisar o código com lupa :
❌ Problemas do ponto de vista do DDD
Entidades com setters públicos
Qualquer código pode quebrar invariantes
Temos um relacionamento bidirecional rico
Um agregado “enxerga” o outro por inteiro
Temos uma mistura de conceito de navegação com domínio
O domínio passa a existir para o ORM
Categoria e Produto estão acoplados fortemente
A Mudança em um impacta diretamente o outro
O Modelo é orientado à persistência
Não existe comportamento definido
Isso é o
que chamamos de modelo anêmico orientado a banco.
O erro conceitual mais importante aqui :
“Este código esta definindo duas
classes Categoria e Produto que possuem um relacionamento
muitos para muitos”
Mas na abordagem do DDD, a pergunta correta é:
Categoria e Produto pertencem ao mesmo Bounded Context ?
O que é Bounded Context (Contexto Delimitado)?
No mundo
real, uma palavra pode significar coisas diferentes dependendo de onde você
está. No desenvolvimento de software, o Bounded Context é a fronteira lógica
onde um modelo de domínio específico e sua Linguagem Ubíqua são aplicáveis e
válidos.
O Problema: Tentar criar um modelo único para a
empresa inteira gera classes gigantescas e confusas.
A Solução:
Dividimos o sistema em contextos menores.
No contexto de Vendas, um
Produto tem preço e estoque.
No contexto de Logística, o mesmo Produto tem
peso, dimensões e tipo de embalagem.
Porque :
Se pertencem ao MESMO contexto, NÃO existe ACL (Anti-Corruption
Layer)
Se pertencem a contextos
diferentes, NÃO pode existir relacionamento direto
O que é ACL (Anti-Corruption Layer)?
A Camada
Anticorrupção(ACL) é uma camada de tradução. Ela é usada quando o seu sistema (o
novo, limpo, seguindo DDD) precisa conversar com outro sistema (um legado, uma
API externa ou outro Bounded Context) que possui um modelo de dados diferente.
Sua função: Impedir que os conceitos e a "sujeira" do modelo externo
"corrompam" o seu modelo de domínio interno.
Como funciona: Ela traduz as
solicitações do seu contexto para o formato que o outro entende e vice-versa,
mantendo o seu domínio isolado e puro.
Nota : ACL é um padrão de design crucial utilizado para servir como uma ponte de comunicação tradutora e isolante entre dois ou domínios que possuem modelos de domínio ou linguagens ubíquas diferentes
Por que isso muda o relacionamento Categoria x Produto?
Como você bem pontuou, a decisão de design depende da fronteira:
Mesmo Contexto (Ex: Catálogo): Se Categoria e Produto vivem juntos, o
relacionamento é direto. O foco aqui é como o negócio organiza os itens.
Contextos Diferentes (Ex: Marketing x Estoque): Se Categoria
pertence ao Marketing e Produto ao Inventário, eles não se conhecem diretamente
via código/banco. A comunicação acontece via Identificadores (IDs) ou Eventos de
Domínio, muitas vezes mediada por uma ACL para garantir que uma mudança no
Marketing não quebre a lógica do Estoque.
Cenário A — Categoria e Produto NO MESMO
Bounded Context
Exemplo: Gestão de
Catálogo
Nesse caso:
❌ NÃO existe ACL
❌ NÃO existe Published
Language (Padrão de integração utilizado para permitir a comunicação entre
diferentes
Contextos Delimitados)
❌ NÃO existe Snapshot (refere-se
a uma representação serializada do estado de um
Agregado (Aggregate) em um determinado momento no tempo)
Porque não há integração entre
contextos.
Modelagem DDD correta (simplificada)
public class Produto { public Guid Id { get; } public string Nome { get; private set; } public Money PrecoAtual { get; private set; } private readonly List<Categoria> _categorias = new(); public IReadOnlyCollection<Categoria> Categorias => _categorias; } public class Categoria { public Guid Id { get; } public string Nome { get; private set; } } |
Observe:
Sem navegação ORM
Sem bidirecionalidade
Sem dependência de infraestrutura
DDD não começa modelando
relacionamentos (1:N, N:N).
DDD começa
modelando responsabilidades e invariantes.
Cenário B — Produto e Categoria em CONTEXTOS
DIFERENTES
Aqui Produto e Categoria
NÃO
estão em contextos diferentes entre si
Eles estão juntos no Catálogo
O
“contextos diferentes” aqui é entre o BC Catálogo e o BC Pedidos
“Onde entra a ACL aqui?”
A ACL é uma camada que protege o seu domínio de modelos, conceitos e regras externas. Lembrando que ACL não é: DTO, API Client , Repositório, Adapter técnico, Mapper genérico (AutoMapper) e vive na camada de aplicação e não no domínio.
A ACL NÃO entra entre Categoria e Produto
Ela entra quando
OUTRO contexto consome dados do Catálogo
Exemplo:
Pedidos consumindo Produto do Catálogo
Catálogo publica (Published
Language):
| public sealed record
ProdutoCatalogoDto( Guid Id, string Nome, decimal Preco ); |
Pedidos recebe isso via ACL:
|
public sealed class
ProdutoCatalogoAcl { public ProdutoSnapshot Traduzir(ProdutoCatalogoDto dto) { return new ProdutoSnapshot( dto.Id, dto.Nome, new PrecoUnitario(dto.Preco) ); } } |
E o domínio de Pedidos usa: pedido.AdicionarItem(produtoSnapshot);
Pedidos nunca aceita Produto do Catálogo
Pedidos só aceita o que ELE
entende
Grave esta regra:
A ACL só existe ENTRE Bounded
Contexts.
Nunca dentro de um mesmo contexto.
Se você tem:
Categoria ↔ Produto no mesmo BC → sem ACL
Catálogo → Pedidos → com
ACL
Clientes → Pedidos → com ACL
A ACL só existe quando atravessamos uma fronteira de Bounded Context.
Dentro do mesmo contexto, não há ACL.
Entre
contextos diferentes, a ACL é obrigatória para proteger o domínio.
E estamos
conversados...
"Porque somos feitura sua, criados em Cristo Jesus
para as boas obras, as quais Deus preparou para que andássemos nelas"
Efésios 2:10
Referências:
NET - Unit of Work - Padrão Unidade de ...