Domain Model
Definição (Fowler)
Seção intitulada “Definição (Fowler)”“An object model of the domain that incorporates both behavior and data.”
Ao contrário do Transaction Script, a lógica não fica em procedimentos externos, mas dentro dos objetos de domínio. Cada objeto conhece suas regras e as impõe; o estado inválido deve ser impossível de representar.
Problema que resolve
Seção intitulada “Problema que resolve”Domínios complexos têm regras que se entrelaçam e mudam com frequência. Espalhá-las por procedimentos gera duplicação e inconsistência. O Domain Model localiza cada regra junto aos dados que ela governa, reduzindo duplicação e tornando as invariantes explícitas.
Quando usar
Seção intitulada “Quando usar”- Regras de negócio complexas, ricas e mutáveis.
- Domínio central do negócio (core domain) que merece investimento.
- Quando invariantes precisam ser garantidas de forma consistente em todos os fluxos.
Trade-offs
Seção intitulada “Trade-offs”| Vantagem | Custo |
|---|---|
| Invariantes locais e explícitas | Custo inicial alto |
| Baixa duplicação de regra | Requer mapeamento O/R (impedância objeto-relacional) |
| Escala com a complexidade | Curva de aprendizado maior |
| Alta testabilidade (POJOs) | Overkill para CRUDs |
Quando NÃO usar
Seção intitulada “Quando NÃO usar”Para lógica rasa/CRUD: o Transaction Script entrega o mesmo resultado com muito menos cerimônia. Aplicar Domain Model a um domínio trivial é sobre-engenharia.
Base do DDD tático
Seção intitulada “Base do DDD tático”O Domain Model é a fundação sobre a qual o Domain-Driven Design tático constrói vocabulário disciplinado:
- Entidades — objetos com identidade e ciclo de vida.
- Value Objects — objetos definidos por valor, imutáveis.
- Agregados — fronteira de consistência que agrupa entidades e VOs sob uma raiz.
- Persistência via Repositórios (não via DAO - Data Access Object orientado a tabela).
classDiagram
class Conta {
-BigDecimal saldo
-Status status
+debitar(valor)
+creditar(valor)
}
class Movimento {
-Tipo tipo
-BigDecimal valor
}
Conta "1" --> "*" Movimento : registra
Alerta: modelo anêmico
Seção intitulada “Alerta: modelo anêmico”O Anemic Domain Model é o anti-padrão em que os objetos têm só getters/setters e nenhum comportamento — toda a regra migra para serviços. Isso é um code smell: mantém a aparência de OO mas com a estrutura de um Transaction Script, sem as vantagens de nenhum dos dois. Fowler o classifica explicitamente como anti-padrão.
Exemplo Java/Spring
Seção intitulada “Exemplo Java/Spring”@Entitypublic class Conta {
@Id private Long id;
private BigDecimal saldo;
@Enumerated(EnumType.STRING) private Status status;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "conta_id") private List<Movimento> movimentos = new ArrayList<>();
protected Conta() {}
public Conta(Long id) { this.id = id; this.saldo = BigDecimal.ZERO; this.status = Status.ATIVA; }
public void debitar(BigDecimal valor) { exigirValorPositivo(valor); exigirAtiva(); if (saldo.compareTo(valor) < 0) { throw new SaldoInsuficienteException(id, saldo, valor); } this.saldo = this.saldo.subtract(valor); this.movimentos.add(new Movimento(Tipo.DEBITO, valor)); }
public void creditar(BigDecimal valor) { exigirValorPositivo(valor); exigirAtiva(); this.saldo = this.saldo.add(valor); this.movimentos.add(new Movimento(Tipo.CREDITO, valor)); }
private void exigirAtiva() { if (status != Status.ATIVA) { throw new ContaInativaException(id); } }
private void exigirValorPositivo(BigDecimal valor) { if (valor == null || valor.signum() <= 0) { throw new IllegalArgumentException("Valor deve ser positivo"); } }
public BigDecimal getSaldo() { return saldo; }}@Servicepublic class TransferenciaService {
private final ContaRepository contas;
public TransferenciaService(ContaRepository contas) { this.contas = contas; }
@Transactional public void transferir(Long origemId, Long destinoId, BigDecimal valor) { Conta origem = contas.findById(origemId).orElseThrow(); Conta destino = contas.findById(destinoId).orElseThrow();
origem.debitar(valor); destino.creditar(valor);
contas.save(origem); contas.save(destino); }}A regra “saldo não pode ficar negativo” e “conta precisa estar ativa” vivem dentro de Conta, garantidas em qualquer fluxo. O serviço apenas orquestra (Service Layer), sem conter regra de domínio.
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Modelo anêmico: entidades só com getter/setter; regra toda no serviço.
- Vazamento de invariante: permitir
setSaldopúblico, contornando as regras dedebitar/creditar. - Agregados grandes demais: fronteiras de consistência inchadas geram contenção transacional.
- Persistência dirigindo o modelo: deixar a modelagem de tabelas ditar as classes de domínio, corrompendo o modelo rico.
- Confundir com Service Layer: o serviço orquestra; o comportamento mora nas entidades.
Relações
Seção intitulada “Relações”- Contraste com Transaction Script.
- Base do Domain-Driven Design tático: Entidades, Value Objects, Agregados, Domain Services, Bounded Context.
- Orquestrado por Service Layer; persistido por Repositórios (não DAO - Data Access Object).
- Modelo anêmico é um code smell; ver Code Smells e SOLID.
- Favorece Testabilidade e Arquitetura.
- Índice: PoEAA.
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. O que distingue um Domain Model de um Transaction Script?
Resposta
O Domain Model coloca comportamento e dados juntos nos objetos, que protegem suas invariantes; o Transaction Script mantém dados passivos e a lógica em procedimentos externos.
2. O que é um modelo anêmico e por que é problemático?
Resposta
É um “Domain Model” com objetos só de dados (getters/setters) e regra nos serviços. Tem o custo estrutural do OO sem os benefícios: nem a simplicidade do Transaction Script nem o encapsulamento do Domain Model. Fowler o trata como anti-padrão.
3. Como o Domain Model se relaciona com o DDD tático?
Resposta
É sua base: Entidades, Value Objects e Agregados são técnicas para construir um Domain Model rico com identidade, imutabilidade e fronteiras de consistência bem definidas, guiado pela linguagem ubíqua.
4. Quando o Domain Model é sobre-engenharia?
Resposta
Em domínios de lógica rasa (CRUDs), onde o custo de mapeamento O/R e a curva de aprendizado não se pagam — ali o Transaction Script é mais adequado.