Home
Do you know when to use value objects?
  v1.0 Posted at 16/04/2019 7:52 AM by Tiago Araujo

When defining a domain, entities are created and consist of properties and methods. The properties represent the internal state of the entity and the methods are the actions that can be performed. The properties typically use primitive types such as strings, numbers, dates, and so on.​​

As an example, consider an AD account. An AD Account consists of a domain name and user name, e.g. SSW\Jason. It is a string so using the string type makes sense. Or does it?

when-use-value-bad.png
Figure: Bad Example - Storing an AD Account as a String (AD Account is a complex type)

An AD Account is a complex type. Only certain strings are valid AD accounts. Sometimes you will want the string representation (SSW\Jason), sometimes you will need the domain name (SSW), and sometimes just the user name (Jason). All of this requires logic and validation, and the logic and validation cannot be provided by the string primitive type. Clearly, what is required is a more complex type such as a value object.

when-use-value-good.png
Figure: Good Example - Storing an AD Account as a Value Object to Support Logic and Validation

The underlying implementation for the AdAccount class is as follows:

when-use-value-good-2.png
Figure: Good Example - Implementation of the AdAccount Value Object Supports Logic and Validation

The AdAccount type is based on the ValueObject type, which you can view here; https://github.com/SSWConsulting/NorthwindTraders/blob/master/Northwind.Domain/Infrastructure/ValueObject.cs.

Working with the AD accounts will now be easy. You can construct a new AdAccount with the factory method For as follows:

when-use-value-eg-1.png

The factory method For ensures only valid AD accounts can be constructed and for invalid AD account strings, exceptions are meaningful, i.e. AdAccountInvalidException rather than IndexOutOfRangeException.

Given an AdAccount named account, you can access:

  1. The domain name using; account.Domain
  2. The user name using; account.Name
  3. The full account name using; account.ToString()

The value object also supports implicit and explicit conversion operators. You can:

  1. Implicitly convert from AdAccount to string using; (string)account
  2. Explicitly convert from string to AdAccount using; (AdAccount)"SSW\\Jason"

If you're using Entity Framework Core, you should also configure the type as follows:

when-use-value-eg-2.png
Figure: Using Entity Framework Core to Configure Value Objects as Owned Entity Types

​With the above configuration in place, EF Core will name the database columns for the properties of the owned entity type as AdAccount_Domain and AdAccount_Name. You can learn more about Owned Entity Types by reviewing the EF Core documentation.​

Next time you are building an entity, consider carefully if the type you are defining is a primitive type or a complex type. Primitive types work well for storing simple state such as first name or order count, complex types work best when defining types that include complex logic or validation such as postal or email addresses. Using a value object to encapsulate logic and validation will simplify your overall design.


Related rules

    Do you feel this rule needs an update?

    If you want to be notified when this rule is updated, please enter your email address:

    Comments: