Value Objects explained

I’ve already written about base entity class. Today, I’d like to continue with Value Object base class I use in my projects. Also, I’ll share some best practices regarding Value Objects implementation.

Value Objects: what is it?

An object that represents a descriptive aspect of the domain with no conceptual identity is called a Value Object. Value Objects are instantiated to represent elements of the design that we care about only for what they are, not who or which they are.

 — Eric Evans

In other words, value objects don’t have their own identity. That is, we don’t care if a value object is the same physical object we had before or another one with the same set of properties. Value Objects are completely interchangeable.

For example, a dime coin would most likely represent a value object. It doesn’t matter what exact piece of metal you have, they all are just 10 cent coins. On the other hand, you may build a system which responsibility is to track every coin produced by a mint. In this case, you do care about which coin a person has because all of them have a unique identity.

Consequently, whether a class is a value object is dictated by your domain and use cases.

Attributes of Value Objects

Let’s specify attributes pertain to Value Objects.

  • No identity. A corollary of value objects' identity-less nature is, obviously, not having an Id property. Unlike entities, value objects should be compared by value, not by identity field.

  • Immutability. As any value object can be replaced by another value object with the same property set, it’s a good idea to make them immutable to simplify working with them, especially in multithread scenarios. Instead of changing an existing value object, just create a new one.

  • Lifetime shortening. It’s another corollary of the value objects' identity-less nature. They can’t exist without a parent entity owning them. In other words, there always must be a composition relationship between a Value Object class and an Entity class. Without it, value objects don’t make any sense.

  • Lifetime shortening leads to another consequence: Value Objects should not have separate tables in database. This one is an attribute programmers find the most controversial.

Developers, especially if they have wide experience with relational databases, tend to store Value Objects in separate tables (or, in case of NoSQL databases, in separate collections). Situation gets worse if there are more than one Entity class owning a Value Object.

For example, both Company and User entities might have a property referring to Address value object. The very first impulse could be extracting the fields concerning Address from Company and User tables to a separate Address table and storing references to it instead. I consider this approach as a bad practice. Not only does it make composition relationship more complex as you have to maintain consistency of two tables instead of one, but also gives developers a false assumption about the Address’s nature. As the Address table must have an Id column, it’s easy to mistake it for an Entity, despite its sole purpose of being a Value Object.

Give me the code!

There’s an implementation for Value Object base class made by Jimmy Bogard I often see developers copy. In short, it allows you to extract equality logic to the base class so that you don’t have to implement it in each Value Object separately. Its Equals() and GetHashCode() methods use reflection to gather information about the fields of a type and perform comparison or object’s hash code calculation.

Although this implementation might be a good solution for a quick scaffolding, it suffers from a fundamental flaw. Equality logic implementation should be a conscious decision. Every Value Object has its own unique property set and comparison strategy. By extracting this logic to a base class, you actually say that all Value Objects are just bags of data with the same behavior, which is not true. It’s worth nothing for a Value Object to have properties that don’t take part in equality logic. It’s too easy to forget to override Equals() and GetHashCode() in such cases.

Well, what should it look like then? Here’s the code I use in my projects:

public abstract class ValueObject<T>
    where T : ValueObject<T>
{
    public override bool Equals(object obj)
    {
        var valueObject = obj as T;
 
        if (ReferenceEquals(valueObject, null))
            return false;
 
        return EqualsCore(valueObject);
    }
 
    protected abstract bool EqualsCore(T other);
 
    public override int GetHashCode()
    {
        return GetHashCodeCore();
    }
 
    protected abstract int GetHashCodeCore();
 
    public static bool operator ==(ValueObject<T> a, ValueObject<T> b)
    {
        if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
            return true;
 
        if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
            return false;
 
        return a.Equals(b);
    }
 
    public static bool operator !=(ValueObject<T> a, ValueObject<T> b)
    {
        return !(a == b);
    }
}

Note that there’s no Id property because, as discussed earlier, Value Objects don’t have their own identity. Also, you might notice that the class doesn’t implement IEquatable<> interface. Instead, EqualsCore() is introduced. With IEquatable, you will have to write null-checking code both in Equals(object obj) and Equals(T obj) methods. Moreover, as Equals(T obj) is made abstract, you will have to copy null checks to all of the ValueObject’s subclasses. By making EqualsCore protected, you can get rid of such checks; all you should do is extract them to the Equals method of your base class.

Let’s take a look at how Address value object could be implemented:

public class Address : ValueObject<Address>
{
    public virtual string Street { get; protected set; }
    public virtual int ZipCode { get; protected set; }
    public virtual string Comment { get; protected set; }
 
    public Address(string street, int zipCode, string comment)
    {
        Contracts.EnsureNotNull(street);
 
        Street = street;
        ZipCode = zipCode;
        Comment = comment;
    }
 
    protected override bool EqualsCore(Address other)
    {
        return Street == other.Street && ZipCode == other.ZipCode;
    }
 
    protected override int GetHashCodeCore()
    {
        return (ZipCode.GetHashCode() * 397) ^ Street.GetHashCode();
    }
}

As you can see, Comment property doesn’t participate in EqualsCore() and GetHashCodeCore() methods because it’s just a user-created note; it can’t affect equality of addresses.

Also, note that properties are made read-only to comply with immutability requirement.

Value Objects and .NET value types

Strictly speaking, there’s no relation between Value Objects and .NET value types because the first is a design concept and the latter is a technical implementation detail.

However, there are a lot of similarity in these notions. Structs in .NET are inherently immutable because they are always passed by value. Also, they are much cheaper than reference types in terms of system resources. Look at DateTime struct from the BCL. It has a clear Value Object semantics: it is immutable and doesn’t have any identity fields.

In theory, you could use .NET value types as Value Objects. However, I wouldn’t recommend it. First of all, structs don’t support inheritance, so you will have to implement equality operators in every struct separately, which will lead to massive code duplication. Also, ORMs often don’t handle mapping to structs well. That said, I recommend you to always use classes for Value Objects.

Summary

Value Object is an important DDD concept. You should clearly show which of your domain classes is an Entity and which is a Value Object by inheriting them from Entity and ValueObject<> respectively.

Subscribe


I don't post everything on my blog. Don't miss smaller tips and updates. Sign up to my mailing list below.

Comments


comments powered by Disqus