Value Objects: pequeños guardianes que hacen grande tu dominio
Los Value Objects son la pieza que separa código que funciona de código que representa el dominio correctamente. Implementación real en Code Finances con validaciones inmutables.
Volvemos con la serie de Code Finances y hoy toca una de esas piezas que parecen pequeñas… pero cambian por completo la calidad de tu diseño.
Si estás trabajando con DDD o simplemente quieres escribir código más robusto, esto te interesa.
🤔 ¿Qué es un Value Object?
Un Value Object es una forma de representar datos del dominio que:
- 🙅♂️ No tienen identidad
- 📋 Se definen por sus atributos
- 🔒 Son inmutables
- 📦 Encapsulan lógica y validaciones
No es un simple string o number. Es un objeto que tiene significado dentro del negocio.
⚠️ El problema sin Value Objects
Cuando no los usamos:
- 🧩 Los primitivos (
string,number…) se reparten por todo el código - ❌ Las validaciones se duplican o se olvidan
- 💣 Aparecen inconsistencias difíciles de detectar
- 🧠 El dominio pierde expresividad
Ejemplo típico:
if (percentage > 100) {
throw new Error('Invalid percentage')
}
¿Dónde vive esa regla? ¿Quién la garantiza siempre?
🚀 La solución: encapsular el conocimiento del dominio
Con Value Objects, movemos esa lógica al lugar correcto:
class LiquidityCategoryPercentage {
constructor(private readonly value: number) {
this.ensureIsValid(value)
}
private ensureIsValid(value: number) {
if (value > 100) {
throw new Error('Percentage cannot be greater than 100')
}
}
public update(newValue: number): LiquidityCategoryPercentage {
return new LiquidityCategoryPercentage(newValue)
}
public getValue(): number {
return this.value
}
}
Ahora la regla vive en un solo sitio, es imposible crear un valor inválido y el dominio se protege a sí mismo.
🧠 ¿Por qué usarlos?
En Code Finances, usar Value Objects nos ha dado:
- ✅ Validación inmediata
- ❌ Menos errores por inconsistencias
- 🗣 Mayor expresividad —
Percentage,Money,Email… - 🧪 Testing más simple
- ♻ Reutilización de lógica
- 🧱 Diseño más sólido y mantenible
Y además cumplen con SOLID de forma natural.
🛠️ Caso real: regla de negocio
En nuestro dominio tenemos una regla clara:
El porcentaje de una categoría de liquidez no puede superar el 100%, porque estarías distribuyendo más revenue del que existe.
Esto no es un detalle técnico. Es conocimiento del negocio. Y por eso debe vivir en un Value Object.
🔄 Actualización segura
Cuando necesitas modificar el valor, no mutas el objeto original. Creas uno nuevo válido:
const updated = percentage.update(80)
- ✔️ Inmutabilidad garantizada
- ✔️ Estado siempre consistente
🧼 Buenas prácticas
- Mantenlos inmutables
- Valida siempre en el constructor
- Evita exponer setters
- Usa nombres del dominio, no técnicos
- Hazlos pequeños y específicos
⚠️ Antipatrones a evitar
- 🚫 Usarlos como simples contenedores sin lógica
- 🚫 Meter demasiada responsabilidad en un solo VO
- 🚫 Saltarse validaciones "por conveniencia"
- 🚫 Convertirlos en entidades encubiertas
💬 Conclusión
flowchart TB
IN["Input primitivo<br/>string / number"] --> VO
subgraph VO[Value Object]
direction TB
V{Validación}
V -->|inválido| ERR[DomainError]
V -->|válido| OBJ[Objeto inmutable]
end
OBJ --> E[Entidad / Agregado]
OBJ -.->|comparación por valor| OBJ2[Otro Value Object]
style VO fill:#1e1b4b,stroke:#818cf8,color:#e0e7ff
style ERR fill:#3b0764,stroke:#a855f7,color:#f3e8ff
style OBJ fill:#0f172a,stroke:#818cf8,color:#e0e7ff
style E fill:#0f172a,stroke:#818cf8,color:#e0e7ff
Los Value Objects son esos pequeños detalles que separan código que funciona de código que representa el dominio correctamente.
Son guardianes silenciosos que aseguran que tus datos:
- ✔️ Siempre sean válidos
- ✔️ Siempre tengan sentido
- ✔️ Siempre respeten las reglas del negocio
Y cuando los adoptas bien… tu diseño cambia por completo.
Publicado originalmente en LinkedIn.