The unknown value of Value Objects

Domain-Driven Design (Evans 2003, Nilsson 2006) established a succinct taxonomy to describe classes, in the context of the upper layers of an application (i.e. non-infrastructure):

  1. Entities: objects that have a unique identity and a non-trivial life cycle (e.g. an invoice in an accounting application)
  2. Services: objects which handle non-trivial operations spanning many objects (e.g. a cargo router)
  3. Value objects: simple objects with no identity (e.g. a phone number)

One of the main rules of DDD is that value objects are immutable. This is often not as self-evident as it seems, as many programmers are not even aware of the state they leak and create. Sadly, state is often positively correlated with entropy. Hence, taking steps to contain and to limit state is one of the keys to taming complexity in an application.

Even if the conceptual complexity of your projects does not warrant an approach as advanced as DDD, using value objects (often called immutable objects outside of DDD) has incredible benefits. Sometimes, most of the complexity in identity-less objects is accidental and related to the way simple operations are handled. To demonstrate this, I will use an example I’ve seen in a project recently. The RationalNumber class represents a rational number with such operations as adding, substracting, multiplying and dividing (only adding and dividing are shown for brevity). Here is a typical use case.

$half = new RationalNumber(1, 2);
$third = new RationalNumber(1, 3);

$half->add($third);
$half->divide($third);

var_dump((string) $half);
var_dump((string) $third);

Without looking at the actual implementation, what would you expect the output to be ? In real-world arithmetic, these are always true:

\dfrac{1}{2} + \dfrac{1}{3} = \dfrac{5}{6}
\dfrac{1}{2} \div \dfrac{1}{3} = \dfrac{3}{2}

In other words, “adding” does not change the nature of the addends, nor does “dividing” change the nature of the divisor and the dividend. It seem fairly intuitive that our implementation of arithmetics follow the same rule, i.e. that the previous code outputs (we’ll come back to how the results of the addition and division are made available a bit later):

string(3) "1/2"
string(3) "1/3"

Well, in PHP, it all depends on how you implement the RationalNumber class. Let’s start with the classic mutable implementation:

class RationalNumber
{
    private $numerator;
    private $denominator;

    public function __construct($numerator, $denominator) {
        $this->setValue($numerator, $denominator);
    }

    public function setValue($numerator, $denominator) {
        $this->numerator = $numerator;

        if ($denominator == 0) {
            throw new DomainException('Denominator cannot be 0');
        }
        $this->denominator = $denominator;
    }

    public function add(RationalNumber $addend) {
        $numerator = $this->numerator * $addend->denominator + $addend->numerator * $this->denominator;
        $denominator = $this->denominator * $addend->denominator;
        $this->setValue($numerator, $denominator);
        return $this;
    }

    public function divide(RationalNumber $divisor) {
        $numerator = $this->numerator * $divisor->denominator;
        $denominator = $this->denominator * $divisor->numerator;
        $this->setValue($numerator, $denominator);
        return $this;
    }

    public function __toString() {
        return sprintf('%d/%d', $this->numerator, $this->denominator);
    }
}

Using the previous code, the output is:

string(3) "15/6"
string(3) "1/3"

The $half object had its state mutated twice, which yields a strange result. Why isn’t the other addend also modified ? Doesn’t it seem odd that an object which we represent using a meaningful name ($half) now means something completely different ? It seems modifying the order of the operations (i.e. calling the add() method on one addend rather than the other) will result in a change in the state of the program. Is this really what we want ?

A programmer which is not well acquainted to the implementation of the RationalNumber class will use the design decision communicated by the public interface to make rational assumptions. Is it that far fetched to represent these arithmetic operations:

a = \dfrac{1}{3} \div \dfrac{1}{2}
b = \dfrac{1}{2} \div \dfrac{1}{3}

Using the code below ?

$half = new RationalNumber(1, 2);
$third = new RationalNumber(1, 3);

$a = $third->add($half);
$b = $half->divide($third);

var_dump((string) $a);
var_dump((string) $b);
var_dump((string) $half);
var_dump((string) $third);

Outputs:

string(3) "5/6"
string(4) "6/10"
string(4) "6/10"
string(3) "5/6"

This all seems contrary to the laws of arithmetic and is far from cohesive and intuitive. What if you do not want to lose the value contained in one of the variables ? For example, representing the following equation is clearly a PITA if the mutable route is used:

(a + b + c) \times a \div b

In fact, a quite profound knowledge of the internals of RationalNumber is necessary to implement the equation correctly. A correct implementation would be:

$aCopy = clone $a;

$a->add($b);
$a->add($c);
$a->multiply($aCopy);
$a->divide($b);

Does that seem right ? The more complex the arithmetic operation to represent is, the more problems arise.

To add to our grief, the implementation has a fundamental flaw. Take a look at it. Twice. Thrice. You probably have not found it. Let me help you:

$d = new RationalNumber(3, 7);
try {
    $d->setValue(3, 0);
}

catch (Exception $e) {
    var_dump((string) $d);
}

Produces output:

string(3) "3/7"

The object ends up in an inconsistent state. Of course, there is an easy way to fix this by moving the guard in setValue() before the first variable assignment. However, the problem is much more profound. This is a typical case of defect introduction/fault isolation effort dichotomy. It is very easy for a small change to create an execution branch which can put the object in an inconsistent state and it is very hard to discover that possibility, even with a test. If that error is to ever happen in staging or even worse, in production, it would percolate very high in the subsequent layers and may have incredible repercussions. The very nature of the implementation is responsible for this dichotomy.

In itself, the mutable implementation is not outright wrong, but it certainly adds accidental complexity to the application and can cause unexpected state inconsistence. Why bother if all this can be avoided if an immutable implementation is used instead ?

class RationalNumber
{
    private $numerator;
    private $denominator;

    public function __construct($numerator, $denominator) {
        if ($denominator == 0) {
            throw new DomainException('Denominator cannot be 0');
        }

        $this->numerator = $numerator;
        $this->denominator = $denominator;
    }

    public function add(RationalNumber $addend) {
        $numerator = $this->numerator * $addend->denominator + $addend->numerator * $this->denominator;
        $denominator = $this->denominator * $addend->denominator;
        return new self($numerator, $denominator);
    }

    public function divide(RationalNumber $number) {
        $numerator = $this->numerator * $number->denominator;
        $denominator = $this->denominator * $number->numerator;
        return new self($numerator, $denominator);
    }

    public function __toString() {
        return sprintf('%d/%d', $this->numerator, $this->denominator);
    }
}

Now, the arithmetic operations:

(a + b + c) \times a \div b

Can be implemented intuitively as:

$result = $a->add($b)->add($c)->multiply($a)->divide($b); 

No cloning is necessary as a new object is created on each step. Also, since we got rid of state mutations, inconsistency can never occur. Any of the initial values can be reused as is, as their state has not mutated.

Always remember that having a mutable state is very heavy in consequences. Permitting state mutation unnecessarily often causes accidental complexity which makes your application more vulnerable to change and introduces completely avoidable risk. The value of value objects lies in their cohesiveness, intuitiveness and simplicity. Use them as often as possible to avoid the pitfalls of accidental mutable state.

About these ads

9 Comments on “The unknown value of Value Objects”

  1. Great post, and definitely shows the pitfalls you can get into with mutable vs. immutable objects. One thing, however, I take issue with is that you appear to be implying that all stateful object operations are bad. I think you have to take a case-by-case look at what you’re trying to accomplish: should the value or state change after a given operation? If so, what are the expectations? Does the method name properly communicate those expectations? If not, do the tests?

  2. Nicolas A. Bérard-Nault says:

    Immutable objects are, indeed, not a silver-bullet.

    Some objects have identities, others don’t. By definition, an object without an identity is interchangeable with another object who has the same “properties”. Its life cycle is only technical and has practically (if any) meaning. In most applications, a user’s email address would be a good example of a value object. The domain does not warrant the observation of the life cycle of the object, which means it can be kept to an absolute minimum in terms of complexity to tame entropy. Another good example, from the ZF this time, would be Zend_Currency (unfortunately, it is mutable, probably in part because it does not respect the SRP).

    The main point is really to avoid creating accidental state whenever possible. Obviously, at some point, some of your objects will carry a state. That is perfectly fine, as long as it is necessary.

  3. […] and, more specifically, how they fit in with domain driven design (DDD). He looks to explain the unknown value of Value Objects to developers that might not know how helpful they really can be. One of the main rules of DDD is […]

  4. edorian says:

    Very nice post. Had a lot of value (haha, sry) for me.

    It’s one of those things where i glanced other it and tough “strange example”. But then while reading it it taught me about something that i never _really_ considered before.

    One example where this got in my way was phps DateTime class and now i have a reference posting to explain that feeling to people ;)

  5. […] and, more specifically, how they fit in with domain driven design (DDD). He looks to explain the unknown value of Value Objects to developers that might not know how helpful they really can be. One of the main rules of DDD is […]

  6. Nick says:

    In itself, the immutable implementation is not outright wrong…

    The word ‘immutable’ should be changed to ‘mutable’ in this sentense.

  7. […] and, more specifically, how they fit in with domain driven design (DDD). He looks to explain the unknown value of Value Objects to developers that might not know how helpful they really can be. One of the main rules of DDD is […]

  8. […] and, more specifically, how they fit in with domain driven design (DDD). He looks to explain the unknown value of Value Objects to developers that might not know how helpful they really can […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.