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...

Primeiro: esse modelo está correto em DDD ?

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 ?

Porque :

Se pertencem ao MESMO contexto, NÃO existe ACL (Anti-Corruption Layer)
Se pertencem a contextos diferentes, NÃO pode existir relacionamento direto

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

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:


José Carlos Macoratti