Pular para o conteúdo

Entidades

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.

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

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”.

  1. Identidade estável e imutável — normalmente um Value Object tipado (PedidoId), não um Long cru. Gere o ID cedo (UUID) para trabalhar com objetos válidos antes da persistência.
  2. equals/hashCode só pelo ID.
  3. Comportamento na Entidade — regras que garantem consistência do próprio objeto ficam nela (modelo rico, não anêmico).
  4. Invariantes protegidas — construtor e métodos validam; sem setters públicos que permitam estados inválidos.

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
  • 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 id cru em vez de PedidoId tipado, 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.

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.