Domínio rico × anêmico
ResumoNo modelo anêmico, entidades são só dados e as regras vivem em serviços — um estilo procedural disfarçado de OOP. No domínio rico, comportamento e dados andam juntos, e as invariantes ficam encapsuladas na entidade.
1. Objetivos de Aprendizagem#
- Reconhecer o anti-padrão do modelo anêmico.
- Mover regras dos serviços para dentro das entidades.
- Saber quando uma regra pertence a um domain service.
2. Pré-requisitos#
- Agregados e invariantes (Aula 44.3).
- Repository e Service (Aula 17.3).
3. Conceito#
O modelo anêmico é aquele em que as entidades só têm get; set; e toda a lógica mora em serviços que manipulam esses dados por fora. Parece OOP, mas é programação procedural: os dados de um lado, o comportamento do outro. Problemas:
- A mesma regra é reimplementada em vários serviços (ou esquecida em um deles).
- Nada impede um estado inválido — qualquer código pode fazer
produto.Preco = -1. - A intenção do negócio some: você lê
service.Update(...), nãoproduto.Reajustar(...).
No domínio rico, o comportamento vive junto dos dados. A entidade expõe métodos que representam operações do negócio e protege suas invariantes — exatamente o "Evite objetos anêmicos" que apareceu lá na Aula 5.1.
Nem toda regra cabe numa entidade. Quando uma operação envolve vários agregados ou não pertence naturalmente a nenhuma entidade, ela vira um domain service — um objeto do domínio, sem estado, nomeado pelo negócio (ex.: PoliticaDePrecos).
4. Mão na Massa#
Antes — anêmico (regra no serviço, entidade burra):
public class Produto // só dados
{
public Guid Id { get; set; }
public string Nome { get; set; }
public decimal Preco { get; set; }
}
public class ReajusteService
{
public void Reajustar(Produto p, decimal percentual)
{
// regra solta aqui; nada impede outro código de setar Preco direto
var novo = p.Preco * (1 + percentual);
if (novo < 0) throw new InvalidOperationException();
p.Preco = novo;
}
}
Depois — domínio rico (a regra vive na entidade):
public class Produto
{
public Guid Id { get; }
public string Nome { get; private set; }
public Dinheiro Preco { get; private set; } // VO da Aula 44.2
public Produto(string nome, Dinheiro preco) { /* valida e atribui */ }
// A operação do negócio, com a invariante protegida internamente.
public void Reajustar(decimal percentual)
{
if (percentual < -1)
throw new ArgumentException("Reajuste tornaria o preço negativo.");
Preco = Preco.Aplicar(percentual);
}
}
O serviço, quando ainda necessário, apenas orquestra (carrega, chama o método do domínio, salva) — ele não contém a regra:
public class CatalogoAppService
{
private readonly IProdutoRepository _repo;
public CatalogoAppService(IProdutoRepository repo) => _repo = repo;
public async Task ReajustarAsync(Guid id, decimal percentual)
{
var produto = await _repo.ObterAsync(id)
?? throw new KeyNotFoundException();
produto.Reajustar(percentual); // a regra está NO domínio
await _repo.SalvarAsync(produto);
}
}
5. Exemplo Completo#
A diferença não é de linhas, é de onde a verdade mora. No modelo rico, Produto é a única autoridade sobre o preço: não existe caminho que produza um produto inválido, porque o set é privado e a única mutação é via Reajustar, que valida. Em revisão de código, você lê a regra uma vez, na entidade.
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
Métodos de negócio na entidade; set privado | Entidade só com get; set; público |
| Serviços de aplicação orquestram (carrega→age→salva) | Serviços que contêm as regras |
| Domain service para regra entre agregados | Enfiar tudo numa entidade só por dogma |
Atenção"domínio rico" não é "entidade gigante". Regras que cruzam agregados, ou que dependem de infraestrutura (ex.: cotação externa), ficam em domain/application services — não dentro da entidade.
7. Segurança e Produção#
- Regras centralizadas na entidade reduzem a chance de um caminho novo (um endpoint, um job) esquecer uma validação — a invariante é imposta pelo próprio tipo, não pela disciplina de quem chama.
8. Exercícios#
- Fácil: aponte, numa entidade sua, um
setpúblico que deveria serprivate. - Médio: mova uma validação de um
Servicepara um método de domínio. - Desafio: modele uma regra "desconto máximo depende da categoria do produto" e decida: método da entidade ou domain service? Justifique.
9. Resumo#
O modelo anêmico separa dados e comportamento e permite estados inválidos; o domínio rico junta os dois e encapsula invariantes. Serviços orquestram; domain services cobrem regras entre agregados.
10. Próximos Passos#
Como tudo isso se encaixa nas camadas: DDD com Onion e CQRS.
11. Referências#
- Martin Fowler — Anemic Domain Model
- Microsoft Learn — Camada de modelo de domínio