Pular para o conteúdo
.NETDomínio rico × anêmico
Módulo 07Domain-Driven Design (DDD)

Domínio rico × anêmico

avancado 35 min de leitura·Atualizado · .NET 9
Resumo

No 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ão produto.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):

C#
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):

C#
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:

C#
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çaEvite
Métodos de negócio na entidade; set privadoEntidade 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 agregadosEnfiar 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 set público que deveria ser private.
  • Médio: mova uma validação de um Service para 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#

44-04 — Domínio rico × anêmico | Curso ASP.NET Core