Domain Centric

View Original

DDD Building Blocks: Value Object

Definition of a Value Object according to Martin Fowler:

A small simple object, like money or a date range, whose equality isn't based on identity

Value Object is an object that represents a concept from your problem Domain. It is important in DDD that Value Objects support and enrich Ubiquitous Language of your Domain. They are not just primitives that represent some values - they are domain citizens that model behaviour of your application.

Good examples of VO mentioned in Martin's definition are Money and Dates. If you are building a GIS application you may come up with Location($lat, $long) Value Object that will encapsulate Latitude/Longitude and so on. The question you are probably asking right now is why is that better than just passing two floats in an array and calling it a $location?

Benefits of using Value Objects

The most important thing is that these objects reflect the language you talk to other developers - when you say Location everyone knows what it means. Second thing is that VO can validate values passed and forbid to construct such object with incorrect data.

Third benefit is fact that you can rely on type - you know that if such VO was passed as an argument it will be always in valid state and you don't need to worry about that. Also VO can contain some specialised methods that only make sense in context of this value and can be attached to this object (no need to create weird Util classes).

Value Object example

To provide an example of Value Object that is common across web applications I've created an EmailAddress:

class EmailAddress
{
    private $address;

    public function __construct($address)
    {
        if (!filter_var($address, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException(sprintf('"%s" is not a valid email', $address));
        }

        $this->address = $address;
    }

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

    public function equals(EmailAddress $address)
    {
        return strtolower((string) $this) === strtolower((string) $address);
    }
}

Above implementation:

  • ensures EmailAddress VO is always in valid state,
  • allows you to type hint it and remove email validation checks (in consequence simplify logic of the application),
  • provides a way of casting to string,
  • provides a method to compare it with other EmailAddress.

Primitive Obsession

You may feel reluctant to use objects as containers for primitive values and such thing is defined as a code smell called "Primitive Obsession":

Primitive Obsession is using primitive data types to represent domain ideas. For example, we use a String to represent a message, an Integer to represent an amount of money, or a Struct/Dictionary/Hash to represent a specific object.

Using Value Objects is one of the strategies of dealing with this smell - key point here is to gather behaviour of data around its instance. Otherwise such behaviour will be scattered across the codebase, may lead to unnecessary complexity and force you to treat defensively values passed to methods.

Immutability

Very important thing to remember and understand about Value Objects is Immutability. Why is that such important concept? Think about reality around you and some values you are using - like number one, colour red and so on. You cannot change those values - it doesn't make sense to change colour red to green and still call it red - it's not red anymore and calling green paint red will confuse people. In the same way Value Objects in your application should be immutable.

For example you have a Meeting entity in your Domain, and this Meeting has some Date (Value Object). Now date of the meeting has changed but not the date itself - 22nd of March is still 22nd of March. It's meeting that requires new Date to be assigned, not Date itself to change - so bin the old Date and create a new one.

Summary

Value Objects are important citizens of your Domain that reflect its concepts. Make sure you will attach related behaviour to such objects which in consequence will make codebase more organised and represent reality better because the key and purpose of Object-Oriented Programming is to model real-world.