Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

01 - Entidade (Entity)

#ciclo-de-vida#ddd#entidade#identidade
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

Entidade (Entity)

Uma entidade é um objeto de domínio definido por sua identidade contínua, e não pelos valores de seus atributos. Dois objetos que representam a mesma conta bancária são a mesma conta, mesmo que o saldo, o limite e o endereço do titular tenham mudado ao longo dos anos. Essa é a diferença fundamental para o objeto de valor, que se define exatamente pelos atributos: dois valores de Dinheiro com o mesmo montante são intercambiáveis, mas duas contas com o mesmo saldo continuam sendo contas diferentes. Entidades vivem ao longo de um ciclo de vida, nascem, sofrem mudanças de estado e morrem, e o software precisa rastrear essa continuidade de forma confiável. Em produção, quase todo conceito que aparece em auditoria, em histórico ou em relacionamento com outros conceitos é uma entidade.

Identidade contínua ao longo do ciclo de vida

A identidade é o que permite dizer que o objeto de hoje é o mesmo de ontem, apesar das mudanças.
Uma assinatura de plano muda de preço, de cartão e até de titular, e continua sendo a mesma
assinatura para fins de cobrança, histórico e suporte. Essa continuidade precisa sobreviver a
processos, a reinícios e a gerações de versões do sistema, então a identidade não pode depender de
memória de processo nem de posição em uma lista. Perder ou duplicar identidade é dos defeitos mais
caros que existem: uma cobrança emitida duas vezes porque dois objetos representavam a mesma fatura
é um incidente clássico, com estorno, multa contratual e auditoria. Por isso a decisão de o que é
entidade e como sua identidade é gerada é decisão de negócio, e não detalhe de implementação.

Igualdade por identidade

Duas entidades são iguais quando compartilham a mesma identidade, ainda que os demais atributos
diferam. Em Java e C#, isso se materializa em sobrescrever equals e hashCode com base no campo de
identidade, e em TypeScript em um método ou função de comparação equivalente. O código a seguir mostra
a implementação típica em Java, com identidade imutável e geração explícita.

public final class Assinatura {
    private final AssinaturaId id;      // identidade imutável, nasce e nunca muda
    private Plano plano;                // atributos podem mudar sem alterar a identidade
    private StatusDaAssinatura status;

    public Assinatura(AssinaturaId id, Plano plano) {
        this.id = id;
        this.plano = plano;
        this.status = StatusDaAssinatura.ATIVA;
    }

    public void suspender(MotivoDaSuspensao motivo) {  // transição com nome de negócio
        if (status != StatusDaAssinatura.ATIVA) throw new SuspensaoInvalida(id, status);
        this.status = StatusDaAssinatura.SUSPENSA;
    }

    @Override
    public boolean equals(Object outro) {
        // igualdade por identidade: dois objetos com o mesmo id são a mesma assinatura
        return outro instanceof Assinatura outra && outra.id.equals(this.id);
    }

    @Override
    public int hashCode() {
        return id.hashCode();           // coerente com equals para uso em coleções
    }
}

O detalhe operacional mais ignorado é o uso em coleções. Se equals e hashCode não forem coerentes,
uma entidade aparece duplicada em um HashSet ou desaparece de um Map, e o defeito só aparece em
cenários específicos de produção. Regras práticas: o campo de identidade é imutável, equals compara
apenas identidade, e hashCode deriva dela.

Como gerar e representar a identidade

Existem três estratégias comuns, cada uma com consequências de produção. Identificadores naturais
usam um atributo que o negócio já garante único, como CPF de titular ou número de contrato emitido
por um sistema oficial; são legíveis, mas dependem de a unicidade ser realmente garantida pelo
processo externo. Identificadores gerados pelo banco, como sequências, são simples e compactos, mas
a identidade só existe depois do insert, o que complica testes e fluxos que precisam do id antes de
persistir. Identificadores gerados pela aplicação, tipicamente UUID, existem desde a criação do
objeto, funcionam offline e simplificam testes, ao custo de índices maiores em tabelas de alto
volume. Em qualquer estratégia, a recomendação de encapsulamento é a mesma: envolver o valor em um
tipo próprio, como AssinaturaId, para impedir que um identificador de um tipo seja confundido com
outro em um parâmetro trocado.

Ciclo de vida, transições e rastreabilidade

O ciclo de vida de uma entidade costuma ser expresso como estados e transições, e os métodos da
classe devem nomear essas transições com a linguagem do negócio: aprovar, suspender, cancelar,
renovar. Cada transição valida se o estado atual permite a mudança, atualiza o estado e, em
sistemas com auditoria ou event sourcing, registra o que aconteceu. Setter público destrói esse
desenho, porque permite pular do estado ATIVA para CANCELADA sem passar pela validação nem pelo
registro. Rastrear mudanças importa em produção por três motivos: auditoria regulatória, que exige
responder quem mudou o quê e quando; diagnóstico de incidentes, que depende de reconstruir a
sequência de estados; e suporte ao cliente, que precisa saber por que uma cobrança foi feita. Um
log de eventos por entidade, mesmo simples, atende os três.

Concorrência e versionamento

Entidades são exatamente os objetos sobre os quais duas pessoas podem agir ao mesmo tempo, então a
concorrência é parte do problema que elas resolvem. O mecanismo padrão em sistemas com banco
relacional é o versionamento otimista: a entidade carrega um número de versão, incrementado a cada
mudança persistida, e a atualização falha se a versão lida não corresponde mais à versão atual. O
custo é pequeno e o ganho é grande, pois o sistema nunca grava por cima de uma mudança que não viu.
Em entidades editadas simultaneamente por operadores humanos, como uma reserva alterada por dois
atendentes, a falha vira uma mensagem clara de reconciliação. Em entidades disputadas por processos
automáticos de alto volume, como uma conta que recebe débitos de várias esteiras, o desenho do
agregado deve evitar a disputa na mesma instância, dividindo a operação em entidades menores.

Entidade versus objeto de valor

Critério Entidade Objeto de valor
Definição Identidade contínua Conjunto de atributos
Igualdade Por identificador Por valores dos atributos
Mutabilidade Estado muda ao longo do ciclo de vida Imutável, mudanças criam nova instância
Ciclo de vida Nasce, muda e é encerrada Sem ciclo de vida, é substituída
Exemplo em banking Conta, empréstimo, contrato Valor monetário, CPF, taxa de juros
Exemplo em e-commerce Pedido, cliente, devolução Endereço, item de catálogo, preço
Persistência típica Linha com chave primária própria Colunas embutidas na entidade dona

A fronteira entre os dois é uma decisão de modelagem, e a mesma coisa pode mudar de classe entre
contextos. Endereço é objeto de valor no checkout e pode virar entidade no cadastro de imóveis de
uma imobiliária, onde cada endereço tem história própria. A pergunta orientadora é se o conceito
precisa ser rastreado individualmente ao longo do tempo; se a resposta é sim, é entidade.

Erros comuns e boas práticas

  • Usar igualdade por atributos em entidades. Duas instâncias do mesmo contrato com valores iguais
    continuam sendo dois contratos, e igualdade por atributos gera cobrança e faturamento duplicados.
  • Deixar a identidade mutável ou trocável. Se o id pode mudar, a continuidade que justifica a
    entidade desaparece junto com a confiabilidade dos relacionamentos.
  • Expor setters para todo atributo. Transições de estado precisam de métodos com nome de negócio,
    validação e registro, e setter destrói essa cadeia.
  • Gerar o identificador no banco quando o fluxo precisa dele antes de persistir. Prefira geração na
    aplicação quando há fluxos assíncronos, testes de unidade ou publicação de eventos.
  • Usar o tipo primitivo cru como identidade em toda a base. Um parâmetro trocado entre
    PedidoId e ClienteId compila sem erro e falha em produção.
  • Tratar toda coisa do domínio como entidade por reflexo. Conceitos sem rastreabilidade individual
    são objetos de valor, e tratá-los como entidade multiplica tabelas e complexidade sem retorno.