Entidades
Definição
Seção intitulada “Definição”Para Eric Evans, uma Entidade é um objeto que possui continuidade e identidade ao longo do tempo, mesmo que seus atributos mudem. Duas Entidades são iguais se, e somente se, têm o mesmo identificador — nunca por igualdade de atributos.
Contraste com o Value Object, que não tem identidade e é comparado estruturalmente. A escolha entre os dois é uma das decisões de modelagem mais importantes do Design Tático.
Entidade vs. Value Object
Seção intitulada “Entidade vs. Value Object”| Critério | Entidade | Value Object |
|---|---|---|
| Identidade | Sim, explícita (ID) | Não |
| Igualdade | Por ID | Estrutural (todos os atributos) |
| Mutabilidade | Muda de estado com o tempo | Imutável |
| Ciclo de vida | Rastreado (criada, alterada, removida) | Substituída inteira |
| Exemplo | Cliente, Pedido, Conta |
CPF, Dinheiro, Endereco |
O problema resolvido
Seção intitulada “O problema resolvido”Modela conceitos que precisam ser rastreados individualmente: um Pedido #1234 continua sendo o mesmo pedido mesmo que mude de status, itens ou endereço de entrega. Sem identidade, seria impossível dizer “este é aquele pedido”.
Regras de design
Seção intitulada “Regras de design”- Identidade estável e imutável — normalmente um Value Object tipado (
PedidoId), não umLongcru. Gere o ID cedo (UUID) para trabalhar com objetos válidos antes da persistência. equals/hashCodesó pelo ID.- Comportamento na Entidade — regras que garantem consistência do próprio objeto ficam nela (modelo rico, não anêmico).
- Invariantes protegidas — construtor e métodos validam; sem setters públicos que permitam estados inválidos.
Exemplo em Java + Spring
Seção intitulada “Exemplo em Java + Spring”Entidade rica: identidade tipada, invariantes protegidas, comportamento. Sem setters anêmicos.
package com.loja.pedido.domain;
import java.util.UUID;
public record PedidoId(UUID valor) { public PedidoId { if (valor == null) { throw new IllegalArgumentException("Id nao pode ser nulo"); } }
public static PedidoId novo() { return new PedidoId(UUID.randomUUID()); }}package com.loja.pedido.domain;
import java.math.BigDecimal;import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.Objects;
public class Pedido {
private final PedidoId id; private final ClienteId clienteId; private final List<ItemPedido> itens; private StatusPedido status;
public Pedido(ClienteId clienteId) { this.id = PedidoId.novo(); this.clienteId = Objects.requireNonNull(clienteId, "Cliente obrigatorio"); this.itens = new ArrayList<>(); this.status = StatusPedido.ABERTO; }
public void adicionarItem(ProdutoId produtoId, int quantidade, BigDecimal precoUnitario) { if (status != StatusPedido.ABERTO) { throw new IllegalStateException("Pedido nao esta aberto"); } if (quantidade <= 0) { throw new IllegalArgumentException("Quantidade deve ser positiva"); } this.itens.add(new ItemPedido(produtoId, quantidade, precoUnitario)); }
public void confirmar() { if (itens.isEmpty()) { throw new IllegalStateException("Pedido sem itens nao pode ser confirmado"); } this.status = StatusPedido.CONFIRMADO; }
public BigDecimal total() { return itens.stream() .map(ItemPedido::subtotal) .reduce(BigDecimal.ZERO, BigDecimal::add); }
public List<ItemPedido> itens() { return Collections.unmodifiableList(itens); }
public PedidoId id() { return id; }
public StatusPedido status() { return status; }
@Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Pedido outro)) { return false; } return id.equals(outro.id); }
@Override public int hashCode() { return id.hashCode(); }}classDiagram
class Pedido {
-PedidoId id
-ClienteId clienteId
-List~ItemPedido~ itens
-StatusPedido status
+adicionarItem(ProdutoId, int, BigDecimal)
+confirmar()
+total() BigDecimal
}
class ItemPedido {
-ProdutoId produtoId
-int quantidade
-BigDecimal precoUnitario
+subtotal() BigDecimal
}
Pedido "1" *-- "*" ItemPedido
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Modelo anêmico: Entidade só com getters/setters e regras num
PedidoService. - Igualdade por atributos: comparar Entidades campo a campo em vez de por ID.
- ID primitivo espalhado:
Long idcru em vez dePedidoIdtipado, permitindo trocar um ID de cliente por um de pedido sem erro de compilação. - Setters públicos abertos: permitem que o objeto entre em estado inválido, violando invariantes.
Relações
Seção intitulada “Relações”- Contrasta com Value Objects
- Forma disciplinada do Domain Model de Fowler
- Costuma ser raiz ou membro de um Agregado
- Persistida via Repositórios
- Pode publicar Domain Events
- Bloco de Design Estratégico e Tático e parte de Domain-Driven Design
- Voltar para Home
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. O que define a igualdade de uma Entidade?
Resposta
A identidade (ID), não os atributos. Duas entidades são iguais se têm o mesmo identificador, mesmo com atributos diferentes.
2. Qual a principal distinção entre Entidade e Value Object?
Resposta
A Entidade tem identidade e ciclo de vida, com igualdade por ID e mutabilidade de estado. O Value Object não tem identidade, é imutável e comparado estruturalmente.
3. Por que usar um tipo como PedidoId em vez de Long?
Resposta
Segurança de tipos e clareza: impede misturar IDs de conceitos diferentes, torna a assinatura autoexplicativa e permite validar/gerar a identidade cedo.
4. Como uma Entidade evita ser anêmica?
Resposta
Concentrando o comportamento e as validações de invariantes nela própria (métodos como adicionarItem, confirmar), em vez de expor setters e delegar regras a um service externo.