codete value objects ultimate weapon in the war against primitives main d9fa1746c6
Codete Blog

Value Objects – An Ultimate Weapon in the War Against Primitives

avatar male f667854eaa

24/04/2017 |

10 min read

Sławomir Jabłeka

Developers around the world have embraced the object-oriented programming paradigm in order to create quality applications. But while trying to model complex structures and behaviors, somehow we forgot to appropriately represent simple values. That’s where value objects come into play.

About Value Objects

Martin Fowler defines value object (VO) in the following way: A small simple object, like money or a date range, whose equality isn’t based on identity. So two value objects are equal not when they refer to the same object, but when the value that they hold is equal, e.g. two different one hundred dollar bills have the same value when we use them to buy something (class Money would be a value object). 

On the other side, we have entities that are compared on identity, e.g. two people can have the same name, but are not the same person (class Person would be an entity), therefore are not equal. It’s very important to note that whether something should be represented as a value object or an entity, depends on the context it’s used in. Let’s look at the Money value object from our first example. For the government, it might be considered as an entity, where each bill is differentiated by its serial number.

Example of simple value object:

PHP

final class Money { /** @var int */ private $value; /** @var string */ private $currency; public function __construct(int $value, string $currency = 'USD') { $this->value = $value; $this->currency = $currency; } public function equals(Money $money): bool { return $this->value === $money->value && $this->currency === $money->currency; } public function getValue(): int { return $this->value; } public function getCurrency(): string { return $this->currency; } }
final class Money

{

    /** @var int */

    private $value;

    /** @var string */

    private $currency;

    public function __construct(int $value, string $currency = 'USD')

    {

        $this->value = $value;

        $this->currency = $currency;

    }

    public function equals(Money $money): bool

    {

        return $this->value === $money->value

            && $this->currency === $money->currency;

    }

    public function getValue(): int

    {

        return $this->value;

    }

    public function getCurrency(): string

    {

        return $this->currency;

    }

}

Example of a simple entity:

PHP

class Person { /** @var string */ private $name; public function __construct(string $name) { $this->name = $name; } public function equals(Person $person): bool { return $this === $person; } public function getName() { return $this->name; } }

class Person
{
    /** @var string */
    private $name;
 
    public function __construct(string $name)
    {
        $this->name = $name;
    }
 
    public function equals(Person $person): bool
    {
        return $this === $person;
    }
 
    public function getName()
    {
        return $this->name;
    }
}

Java

Of course in a real application Person would probably have some identity other than the object’s reference (e.g. Social Security number).

We often hear about value objects in the context of Domain-Driven Design (DDD in short) building blocks. And while I encourage everyone to get familiar with the DDD concept, value objects are not tied to DDD and can be used outside of it.

How to use Value Objects?

In practical terms, you can think of value objects as your own primitive types. And they should behave like ones. It means that value objects should always be immutable! This is crucial for languages that don’t support custom value types (e.g. PHP, Java), otherwise, it’s hard to avoid issues with values changing unexpectedly (values should be replaced, not modified!).

When should value objects be used? Basically every time we have some value that has a special meaning in our business logic, even when it could be represented by a primitive type and especially if there are some limitations on the value it’d hold. For example, an email address can be represented by a simple string, but it’s better to use a value object (I’ll list all benefits of VOs a bit later, so keep reading!). 

And of course, our value object can have more than one internal value (like in the Money class example where we store both value and currency).

In addition to standard getter methods for internal state retrieval, VOs can have helper methods with various functionality. This includes creating new values (either entirely new or based on the current one, e.g. result of adding some amount to Money object), providing different representation for its internal state (e.g. IPAddress VO can present IP address as a string or binary data), checking if value object meets some condition (e.g. DateRange VO can have a method to find out if it overlaps another DateRange object), etc. 

It’s important to remember about immutability (none of these methods can modify the internal state) and not to put any actual business logic inside value objects – they should be as self-contained as possible.

Example of additional VO method:

PHP

final class Money { // Money definition from previous example should be here public function add(Money $toAdd): Money { if ($this->currency !== $toAdd->currency) { throw new InvalidArgumentException("You can only add money with the same currency"); } if ($toAdd->value === 0) { return $this; } return new Money($this->value + $toAdd->value, $this->currency); } }

final class Money
{
    // Money definition from previous example should be here
 
    public function add(Money $toAdd): Money
    {
        if ($this->currency !== $toAdd->currency) {
            throw new InvalidArgumentException("You can only add money with the same currency");
        }
        if ($toAdd->value === 0) {
            return $this;
        }
        return new Money($this->value + $toAdd->value, $this->currency);
    }
}

Making Value Object shorter

Unfortunately, as we can see in our Money examples, defining value objects requires quite a lot of additional code in some languages. While in the case of PHP it’s not that bad and can be easy to overcome with the use of modern IDEs, in Java even the simplest value object definition can look somewhat complex due to the necessity of reimplementing standard equals and hashCode methods (in the example above Google’s Guava library is used to compute the hashCode result). Of course, we still can leverage IDE and code templates, but there are some better alternatives.

Project Lombok Value – probably the most popular way to create value object classes in Java. It’s easy to use and the resulting code is very short:

Java

import javax.annotation.Nonnull; import lombok.Value; @Value public class Money { int value; @Nonnull String currency; public static Money of(int value) { return new Money(value, "USD"); } }
import javax.annotation.Nonnull;
import lombok.Value;
 
@Value
public class Money {
    int value;
    @Nonnull
    String currency;
 
    public static Money of(int value) {
        return new Money(value, "USD");
    }
}

There are some shortcomings though – you can read about them in the AutoValue presentation linked in the next section.

Google AutoValue – another option to make a definition of value objects in Java a lot simpler is AutoValue. It requires a bit of setup, but results in much cleaner and more readable code:

import com.google.auto.value.AutoValue; @AutoValue public abstract class Money { public static Money create(int value, String currency) { return new AutoValue_Money(value, currency); } public static Money create(int value) { return create(value, "USD"); } abstract int getValue(); abstract String getCurrency(); }

import com.google.auto.value.AutoValue;
@AutoValue
public abstract class Money {
public static Money create(int value, String currency) {
return new AutoValue_Money(value, currency);
}
 
public static Money create(int value) {
return create(value, "USD");
}
 
abstract int getValue();
abstract String getCurrency();
}

To learn more I recommend checking out AutoValue: what, why, and how? presentation.

Kotlin data classes – this is the best solution in my opinion. Since Kotlin has 100% Java interoperability we can add it with ease to existing Java projects. We can’t get anything much simpler than this:

data class Money(val value: Int, val currency: String = "USD")

If you want to know more about Kotlin and data classes see our Yet Another Kotlin Tutorial for Android Developers – Part 2 blog post (which is great not only for Android developers).

Validation of Value Object

When creating a new value object we need to make sure that it has a valid state. Let’s take currency from our Money example. Right now we’re using primitive string type to represent it. But we know that it should always consist of three uppercase letters, so why not convert it to its own VO:

PHP

final class Currency { /** @var string */ private $currency; public function __construct(string $currency) { $currency = strtoupper($currency); if (strlen($currency) !== 3 || !ctype_alpha($currency)) { throw new InvalidArgumentException("Currency has to consist of three letters"); } $this->currency = $currency; } // Rest of the definition should be here }

final class Currency
{
    /** @var string */
    private $currency;
 
    public function __construct(string $currency)
    {
        $currency = strtoupper($currency);
        if (strlen($currency) !== 3 || !ctype_alpha($currency)) {
            throw new InvalidArgumentException("Currency has to consist of three letters");
        }
        $this->currency = $currency;
    }
 
    // Rest of the definition should be here
}

Java

Here we only make sure that the currency code has a correct format, but we could also validate it against ISO 4217 currency list (or its subset).

The validation that takes place in a value object should only be concerned with the value itself and should ensure that its state is valid. For example, EmailAddress VO should validate if the given string is a correct email address, but shouldn’t check if this address is blacklisted in our application.

Persistence

Another place that will require some extra code when working with value objects is the persistence of entities that use them. Fortunately the most popular ORMs for both PHP and Java (like Doctrine ORM and Hibernate ORM) support embedding of VOs which makes them easy to configure and reuse across entities. For examples see Doctrine ORM and Hibernate ORM documentation pages.

Benefits of using Value object

There are a few substantial benefits of using value objects:

  • Readability – the types of things that we pass to different parts of our application match what they represent in our domain and it’s clearer what kind of value is expected without having to check in the code
  • Validation – every existing value object is in a correct state. This means that we don’t have to check them when we use them (of course they still need to conform to our business rules)
  • Type hinting (for PHP < 7.0) – while in PHP 7.0+ we can type hint primitive types for parameters, it’s only possible for class types (which VOs are in PHP) in older versions
  • Immutability – value objects’ definition doesn’t require them to be immutable, but in practice, immutability is needed… which is great, because immutability brings thread safety and makes the code easier to reason about (which means less bugs)

Untapped value

Value objects are quite simple both in concept and implementation, yet can make any codebase more readable and organized. Like most good patterns they require some additional work and getting used to, but the initial time investment will save you and your team a lot of work in the future. So leave primitives behind (where it makes sense) and start using value objects today!

Rated: 5.0 / 2 opinions
avatar male f667854eaa

Sławomir Jabłeka

Java/PHP developer. Interested in domain-driven design approach, distributed computing using Akka, and modifying the Java compiler for fun.

Our mission is to accelerate your growth through technology

Contact us

Codete Global
Spółka z ograniczoną odpowiedzialnością

Na Zjeździe 11
30-527 Kraków

NIP (VAT-ID): PL6762460401
REGON: 122745429
KRS: 0000983688

Get in Touch
  • icon facebook
  • icon linkedin
  • icon instagram
  • icon youtube
Offices
  • Kraków

    Na Zjeździe 11
    30-527 Kraków
    Poland

  • Lublin

    Wojciechowska 7E
    20-704 Lublin
    Poland

  • Berlin

    Bouchéstraße 12
    12435 Berlin
    Germany