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:
NET - Unit of Work - Padrão Unidade de ...