En DDD (domain driven design) los value objects son objetos simples que cuantifican, miden o describen algo que pertenece a nuestro dominio. Representan un valor y es su valor lo que importa y lo que los diferencia de otros value objects.

Hay dos aspectos a tener en cuenta para entenderlo mejor:

  1. Como dice Ward Cunningham, es una medida o una descripción de algo.
  2. Su identidad se define a través de su estado y no usando un campo identificador único. Así que, como dice Martin Fowler, dos value objects son iguales si sus atributos contienen los mismos valores. Deben ser inmutables.

Algunos ejemplos de value objects pueden ser:

  • Una fecha
  • Dinero
  • Un nombre
  • Una dirección
  • Una cantidad

Un ejemplo concreto

Por ejemplo, en una receta de cocina, podríamos tener un value object que representara la cantidad “250 gramos”. En realidad esa instancia concreta de la cantidad “250 gramos” no importa demasiado en si misma, si no es a través de su valor. En tanto que objeto inmutable, sea esta u otra instancia, lo importante es el valor que tiene. Será igual a otra con el valor “250 gramos”.

Si necesitáramos representar el valor “1 kilogramo”, aunque también se trata de una cantidad, en lugar de modificar la instancia previa, crearemos una nueva.

Una ventaja obvia de conceptualizar estas descripciones bajo la forma de value objects, es que todo el comportamiento relativo a ellos puede ser implementado dentro de esas clases, por ejemplo una validación. De esta forma tenemos otra ventaja: luchamos contra el anti-pattern de los modelos de dominio anémicos.

Show me the code

Me gustaría traducir en código el ejemplo de la cantidad para los ingredientes de una receta. Por ejemplo “250 gramos” podría ser resuelto con una clase que guarde el tipo de medida (gramos, kilos, mililitros…) y otra que une este concepto a una cantidad (1, 250, 3.5).

Ambas son value objects, en este caso miden la cantidad necesaria de un ingrediente en una receta.

class Measure
{

    private $name;

    public function __construct($name)
    {
        $this->setName($name);
    }

    private function setName($name)
    {
        //implements some validation
        $this->name = $name;
    }

    public function name()
    {
        return $this->name;
    }
}
 

class Quantity
{

    private $amount;
    private $measure;

    public function __construct($amount, Measure $measure)
    {
        $this->setAmount($amount);
        $this->setMeasure($measure);
    }

    private function setAmount($amount)
    {
        $this->amount = floatval($amount);
    }

    private function setMeasure(Measure $measure)
    {
        $this->measure = $measure;
    }

    public function amount()
    {
        return $this->amount;
    }

    public function Measure()
    {
        return $this->measure;
    }
}