Porque não usar repositório genérico com DDD


     Neste artigo veremos alguns motivos para não usar um repositório genérico com a abordagem do Domain Driven Design.

Quando começamos a estudar DDD, é comum reaproveitar padrões que já conhecemos — e um dos mais frequentes é o repositório genérico (IRepository<T>).  À primeira vista, ele parece elegante, reutilizável e “bem arquitetado”.



Mas quando entramos de fato no DDD tático, percebemos que esse padrão entra em conflito direto com os princípios do domínio.

1- O que é um Repositório Genérico?

Um repositório genérico normalmente se parece com isto:
public interface IRepository<T>
{
  Task<T?> GetByIdAsync(Guid id);
  Task AddAsync(T entity);
  Task UpdateAsync(T entity);
  Task DeleteAsync(T entity);
}

Ele parte da ideia de que: “Todas as entidades persistidas se comportam da mesma forma.”

E isso é exatamente o problema em DDD.

2- Por que o Repositório Genérico parece atraente?

É importante reconhecer por que ele é tão usado:
    Reduz código repetido
    Facilita integração com ORM
    Funciona bem em CRUD
    Parece aderir ao princípio DRY


Tudo isso é verdade — fora do DDD.

DDD não otimiza para reutilização técnica,
DDD otimiza para clareza de domínio.

3- O primeiro problema: o Repositório Genérico apaga o domínio

Compare os dois contratos:

IRepository<Produto>  vs   IProdutoRepository

No primeiro caso, o que o contrato comunica?

Nada sobre Produto.
Nada sobre regras.
Nada sobre comportamento.


Em DDD, nomes importam.
Eles fazem parte da linguagem ubíqua.

Um repositório genérico fala de infraestrutura, não de negócio.

4- Segundo problema: ele incentiva CRUD disfarçado

A definição de IRepository<T> quase sempre evolui para isso:

public interface IRepository<T>
{
   Task AddAsync(T entity);
   Task UpdateAsync(T entity);
   Task DeleteAsync(T entity);
   Task<IEnumerable<T>> GetAllAsync();
}

Veja o contrato acima. O que ele comunica ?

“Existe alguma coisa genérica”
“Essa coisa pode ser salva”
“Essa coisa pode ser apagada”


Isso não descreve o negócio,
isso descreve operações técnicas de persistência.

Ou seja:  CRUD, não domínio.

Isso o empurra o sistema para:
   AtualizarProduto
   SalvarCliente
   ExcluirCategoria


Ou seja: CRUD com outro nome.

DDD, por outro lado, trabalha com intenções explícitas:

BloquearCliente
AjustarEstoque
DefinirEnderecoPrincipal

CRUD trabalha com dados.
DDD trabalha com decisões.

5- O terceiro problema: Delete quase nunca faz sentido no domínio

Geralmente em um domínio temos que :
  Um Cliente → é Bloqueado
  Um Produto → é Inativado
  Uma Categoria → é Inativada

Mas o repositório genérico sempre conduz a usar:  Task DeleteAsync(T entity);

Isso induz o desenvolvedor a apagar o que o negócio nunca apaga.

Entretanto o DDD prefere estado, não exclusão física.

6 - O quarto problema: regras começam a vazar

Quando tudo vira genérico, regras começam a aparecer onde não deveriam:

if (!cliente.Ativo)
     throw new Exception();

Ou:
   repository.Update(cliente);

sem passar por comportamento nenhum.

Essa linha é uma regra de negócio disfarçada:
  “Se o cliente não estiver ativo, alguma ação não pode acontecer.”

Essa regra pertence ao domínio. Mais especificamente, ao Aggregate Root Cliente.

Mas ela apareceu fora dele.

Resultado:
    Domínio anêmico
    Lógica espalhada
    Invariantes frágeis

7 - Onde o Repositório Genérico costuma funcionar (e por quê)

É importante ser honesto:

O repositório genérico funciona bem quando:
    O sistema é CRUD
    O modelo é anêmico
    O foco é banco de dados
    DDD é usado apenas como organização de pastas

Isso não é errado,
mas não é DDD tático.

✅ A alternativa correta em DDD: Repositórios Específicos

DDD propõe algo diferente:  Cada Aggregate Root tem seu próprio repositório, explícito e semântico.

8- Exemplo de Repositório Específico (DDD)

Considerando o Produto :

public interface IProdutoRepository
{
    Task<Produto?> ObterPorIdAsync(Guid produtoId,
       CancellationToken cancellationToken = default);

    Task AdicionarAsync( Produto produto,
       CancellationToken cancellationToken = default);

    Task AtualizarAsync(Produto produto,
        CancellationToken cancellationToken = default);
}

Por que isso é melhor?

O contrato fala a linguagem do domínio
    Não existe Delete
    Não existe GetAll
    O repositório só faz o mínimo necessário


Ele existe para servir os casos de uso, não para facilitar consultas.

9 -  Outro exemplo: Considerando Cliente

public interface IClienteRepository
{
   Task<Cliente?> ObterPorIdAsync(Guid clienteId,
       CancellationToken cancellationToken = default);

   Task AdicionarAsync(Cliente cliente,
       CancellationToken cancellationToken = default);

    Task AtualizarAsync( Cliente cliente,
      CancellationToken cancellationToken = default);
}

Observe:
    Endereço não tem repositório
    Tudo passa pelo Cliente
    O limite transacional é respeitado   

10 - “Mas isso não gera código repetido?”

Sim.  E isso é intencional.

O DDD prefere:  Duplicação explícita de contratos em vez de abstração genérica que esconde o domínio.

Código repetido aqui não é cheiro ruim, é clareza arquitetural.

11-  Onde reutilizar código então?

Na infraestrutura, não no domínio.

Exemplo válido:
abstract class EfRepositoryBase<T>
{
    // código comum de ORM
}


Importante:
     Essa classe não é visível para o domínio
     Ela não é contrato
     Ela não dita o modelo

EfRepositoryBase<T> NÃO é usado pelo domínio nem pela aplicação.
Ele é usado apenas pela infraestrutura, como classe base técnica.

12 - Repositório é diferente de Query

Outro ponto essencial:
   Os Repositórios são usados para escrita
   As Queries são usadas para leitura

Por isso você não vê métodos como:
   BuscarPorCpf
   BuscarPorCodigo
   ListarAtivos

Eles virão depois, como Queries, retornando DTOs, sem tocar no Aggregate Root.

“Repositórios existem para proteger o domínio, não para facilitar consultas.”
“Quando tudo é genérico, nada expressa o negócio.”

✅ Conclusão

❌ O Repositório genérico favorece o CRUD
❌ Esconde a linguagem do domínio
❌ Enfraquece as invariantes

✅ Os Repositórios específicos reforçam o modelo
✅ Tornam os casos de uso explícitos
✅ São mais alinhados ao DDD tático

Se você está usando DDD de verdade, o repositório precisa falar a língua do domínio — e não a do ORM.

E estamos conversados...  

"Faze-nos voltar, Senhor Deus dos Exércitos; faze resplandecer o teu rosto, e seremos salvos. "
Salmos 80:19

Referências:


José Carlos Macoratti