Skip to main content

Value Object

Visão Geral

Um objeto de valor é definido pelos seus atributos, não por identidade. Dois objetos com os mesmos valores são intercambiáveis.

É o bloco tático de melhor relação entre esforço e retorno — e o mais subutilizado.

Problema

Conceitos do domínio representados por tipos primitivos espalham responsabilidade e permitem erros que o compilador poderia impedir.

void transferir(String origem, String destino, BigDecimal valor)

Três problemas nessa assinatura.

Troca silenciosa. Nada impede passar destino no lugar de origem — são do mesmo tipo.

Validação espalhada. Onde se verifica que a conta é válida? Em cada chamador, com variações.

Semântica ausente. BigDecimal não sabe que dinheiro tem moeda, que não se soma valores de moedas diferentes, e que o arredondamento segue regra específica.

É o primitive obsession do catálogo de code smells, e objetos de valor são a correção.

Conceitos Centrais

Imutabilidade é requisito

Um objeto de valor não muda. Operações devolvem novos objetos:

salario.acrescido(reajuste) → novo Dinheiro
periodo.estendidoAte(data) → novo Periodo

Isso elimina uma classe inteira de defeitos: não há como alterar um valor compartilhado por acidente, e não há necessidade de cópia defensiva.

Válido por construção

O construtor valida. Se Cpf existe, ele é válido — não há como criar um inválido.

Isso concentra a validação em um lugar e a torna impossível de esquecer. Quem recebe um Cpf não precisa verificar nada.

O ganho é maior do que parece: elimina a verificação defensiva espalhada e o tratamento do caso "e se estiver inválido?" em cada consumidor.

Comportamento pertence ao valor

Um objeto de valor não é só um invólucro de dado. Ele carrega as operações do conceito.

Dinheiro sabe somar apenas com a mesma moeda, sabe ratear com regra de arredondamento definida, sabe comparar. Periodo sabe se contém uma data, se sobrepõe outro período, quantos dias úteis tem.

Um objeto de valor sem comportamento é um invólucro com pouco retorno.

Onde eles aparecem no domínio

Os candidatos são reconhecíveis: dinheiro, quantidade com unidade, período, intervalo, endereço, documento, código, coordenada, percentual, faixa.

Regra prática: qualquer conceito que o negócio nomeia e que hoje é um primitivo ou um grupo de primitivos que andam juntos.

O segundo caso — data clumps — é o mais frequente: dataInicio e dataFim sempre passados juntos são um Periodo.

Quando Usar

  • Um conceito do domínio está representado por primitivo.
  • Vários primitivos sempre andam juntos.
  • Há validação que se repete em vários lugares.
  • Há regra sobre o conceito — arredondamento, comparação, formatação — espalhada.
  • Trocar dois parâmetros do mesmo tipo é um erro possível.

Quando Não Usar

Quando o conceito tem identidade. É entidade.

Quando não há comportamento nem validação. Um invólucro puro sobre uma cadeia de caracteres, sem nenhuma regra, adiciona cerimônia sem retorno. Vale quando há pelo menos validação.

Em subdomínios genéricos ou de apoio. A cerimônia não se paga fora do core.

Quando a linguagem torna caro. Em plataformas onde cada objeto tem custo significativo e o volume é enorme, o impacto precisa ser medido — embora isso seja menos comum do que se supõe.

Para dados de transporte. Um DTO de API não precisa de objetos de valor internos.

Alternativas

  • Tipo primitivo com validação centralizada — menos seguro, mais barato.
  • Alias de tipo — em linguagens que oferecem tipos nominais leves, dá segurança de tipo sem classe.
  • Registro imutável — quando há valores agrupados e pouco comportamento.

Trade-offs

Objeto de valorPrimitivo
Válido por construçãoValidação em cada uso
Troca de parâmetro impossívelSilenciosa
Comportamento junto do conceitoEspalhado
Semântica explícita na assinaturaAusente
Mais tipos no sistemaMenos
Conversão na fronteiraDireto

A última linha é o custo real: objetos de valor precisam ser convertidos ao atravessar a fronteira do domínio — para persistência, para API. É trabalho de mapeamento que os primitivos não exigem.

Modos de Falha

Objeto de valor mutável. Perde as garantias e reintroduz o risco de compartilhamento.

Igualdade não implementada. Comparação por referência faz dois valores iguais parecerem diferentes — e quebra coleções e caches silenciosamente.

Invólucro anêmico. Sem validação nem comportamento.

Explosão de tipos. Um objeto de valor para cada campo, incluindo os que não têm regra.

Vazamento para a API. O tipo do domínio na fronteira externa amarra o contrato público ao modelo interno.

Erros Comuns

Não implementar igualdade e código de dispersão. O defeito mais comum e o mais sutil.

Criar invólucro sem regra.

Torná-lo mutável.

Usar em todo lugar, inclusive fora do core.

Exemplo Real

Um sistema de folha de pagamento tinha BigDecimal para valores monetários e double para percentuais.

Dois defeitos em produção, ambos rastreados à ausência de objetos de valor.

O primeiro: um cálculo de rateio de férias arredondava a cada parcela, e a soma das parcelas diferia do total em centavos. Multiplicado por 4 mil funcionários, gerou divergência contábil que levou uma semana para diagnosticar.

O segundo: um percentual foi passado como 0.05 num lugar e 5 em outro — os dois double, nenhum erro de compilação. O desconto saiu 100 vezes maior para 12 funcionários.

A introdução de Dinheiro e Percentual resolveu os dois.

Dinheiro tem ratear(int partes) que distribui o resto de forma determinística — a última parcela absorve a diferença — e garante que a soma das partes é sempre o total. A regra ficou em um lugar, testada.

Percentual só se constrói a partir de um valor com unidade explícita: Percentual.deCemAvos(5) ou Percentual.deFracao(0.05). A ambiguidade desapareceu na assinatura.

Custo da mudança: duas semanas, incluindo o mapeamento na fronteira de persistência. Nos três anos seguintes, nenhum defeito da mesma categoria.

Conceitos Relacionados

Exercício Prático

Procure no seu domínio parâmetros do mesmo tipo primitivo que aparecem juntos numa assinatura — dois String, duas datas, dois números.

Cada par é uma troca silenciosa possível, e um candidato a objeto de valor.

Depois procure validações que se repetem: cada uma que aparece em mais de dois lugares pertence a um objeto de valor.

Perguntas de Entrevista

  • O que caracteriza um objeto de valor?
  • Por que a imutabilidade é requisito?
  • Qual o risco de não implementar igualdade corretamente?

Para Aprofundar

  • Evans, Eric. Domain-Driven Design. Addison-Wesley, 2003.
  • Vernon, Vaughn. Implementing Domain-Driven Design. Addison-Wesley, 2013.
  • Fowler, Martin. ValueObject, 2016.