Implementing Value Objects
When reasoning about business domains in projects with customers, domain objects and their services are always discussed. But if you listen more closely you might hear additional requirements on the domain. “Off course, customers can also find book by typing in their ISBN” or “we also register the customer’s website, by adding the URL.” Both Isbn and Url have meaning to the customer’s domain. As most of us are aware, both types are stronger than just a character array in the database.
There are validations to be met. Not all strings represent a valid ISBN or a valid URL. Although these well known examples are easily identified, quite often there are more hidden features of this kind that you might not notice at first glance, if you are not an expert in the specific business domain. Think of an employee number each new employee is associated with, or specific types of passwords. During a recent training I presented in Amsterdam (on UML), a software architect from the Dutch tax and customs administration (DTCA) explained that they hardly ever use base types such as int, string or even DateTime, but declare value objects for just about everything. His argument was that we know more about almost all domain object properties than that they’re just a string or a number.
Reference objects and value objects¶
These types such as Isbn, Url, but also social security number, or e-mail address are often referred to as value types, or as Martin Fowler and Eric Evans put it, as value objects, as opposed to your domain objects as Customer or Product, which are examples of reference objects.
Although these examples are clear, distinguishing between value objects and reference objects is not that evident. There are no strict criteria to be met. You might say that a value object should always be small, and a reference object can grow, depending on the amount of properties and services they offer. Big deal.
The key difference between these two types of objects lies in the way they handle equality. Reference objects are said to have object identity, no matter how this is implemented – for instance, whether they refer to the same object in memory, or based on a possible Id property, such as the domain objects in this book. Value objects, on the other hand, validate equality solely on the value of their properties. Two different instances of the Email class are thus considered equal if their properties match. Consider the following code example, that implements (part) of a value object called Email.
private string value;
public Email(string address)
{
value = null;
if (string.IsNullOrEmpty(address)) return;
if (!IsValidEmail(address))
throw new FormatException("{0} is not an email");
value = address;
}
In the next code example, the instances first and second are created. Considering they both get the value aahoogendoorn@gmail.com they should be considered equal, because Email is implemented as a struct (which is a value type in .NET). If Email would have been implemented as a reference object, by default I would only consider two instances to be equal if they refer to the same object in memory.
Email first = new Email(“aahoogendoorn@gmail.com”);
Email second = new Email(“aahoogendoorn@gmail.com”);
If (first.Equals(second)) MessageBox.Show(“Email addresses are equal”);
The big issue here is: should I implement my value objects either as value types (as a struct) or as reference types (as a class)? In general, you should consider implementing your value object as a struct if:
- Your value object represents a single value such as Email, PostalCode, or Isbn.
- You can assure that instances of your value type are immutable.
- Instances of your value type have a small footprint (ideally 16 bytes or less).
- Instances are short-lived.
- Instances are mostly embedded in other objects or serve as argument or return type to methods.
- Instances do not require frequent boxing and unboxing
Otherwise, you should consider implementing your value objects in a class, not in a struct. In other words, simple value objects, such as PostalCode, or Isbn, are implemented as struct, whereas more complex types, such as Money (that includes currencies and different numeric formats) are probebly better off being implemented as a class.
Recipe
Here’s the recipe for building values objects.
- Identify the value object you need, which is in most cases derived from the domain model. Give the new value object a proper name, such as Email, Password, or SubscriptionNumber.
- Think of how the internal value for your value object can best be represented.
- Program the validations and checks required to validate whether a value presented is actually valid.
- Consider if a null or an empty value should be allowed for your value object.
- Implement the proper interfaces for your value object, such as IComparable, and the generic interfaces IComparable to compare instances of your value object and IEquatable to see whether two instances are equal.
- Implement any additional accessor methods your type requires, including the TryParse() pattern, but make sure your value object is immutable.
- Consider whether your new type should be a value type or a reference type.
Identify your value object
During the analysis and design stages of your projects, independent from whether they are agile or waterfall styled, all kinds of value object pop up, mostly during conversations with your users and customers, just like the above mentioned examples for Isbn and Url. Although these examples are very straightforward, your company or customer is likely to have much more of these types, even if you don’t recognise them upfront.
For instance, although I’ve been working with value objects for years, it was only recently that a collegue pointed out to me that the properties for personal names are not really strings. There are lots of characters in out character set that are not allowed in personal names, such as ampersands, quotes or square brackets. Why didn’t I think of this before?
Tip Give a value object a clear and future proof name that is also clearly recognizable by the business.
Once identified, it is key to give the new value object a proper name. With value objects this is even more essential than with other types of objects, because they are pretty easy to re-use in your future projects. Hence, the name should be a future proof as possible in this business.
Consider the internal value
Value objects are compared by the value they represent, and by whether they point to the same address in memory. So next you should consider the type you will use to will hold the internal value of your value object very carefully. In my own experience, most value objects, such as Email, PostalCode, or even SocialSecurityNumber can all be expressed using a string. Although off course they could all be expressed as object, string gives me more comfort, especially when checking for equality. With string, at least you know that two instances of your value object cast to the same type.
However, there are also types that will have another type of internal value, such as DateTime like value objects. I once had a value object called DateTimeInPast, with behaviour you will be able to guess yourself. It had a DateTime typed internal value. By the way, the DateTime class itself, which is also a value object, uses a ulong to represent its internal value (in ticks).
Tip Consider making the internal value of your value object private so nobody from the outside of your objects will be able to damage it.
Validate your value objects
The next step is to identify how new values can best be checked if they really are a bank account number, a URL, or an email address. There are several ways of doing so, depending on the value object at hand. For instance, checking a Dutch postal code is easy. It is represented using a four digit number, of which the first digit can not be zero, followed by two alphabetic characters. You could easily check this using a regular expression, like in the code example below.
However simple, this regular expression will not tell you whether the postal code you pass to the constructor exists in real life. Although the syntax may be correct, there is guarantee that there is a street somewhere in the Netherlands with this postal code. Only if you have the complete postal code register at your disposal, you will be able to validate existence.
There is a trade-off here. Do I check syntax validaty, or value correctness? Off course, the first is fast but unprecise; the latter is precise but more costly. Choosing between the two alternatives is less akward than you might think. With the case of the Dutch postal codes it appears to be black or white, but validating the syntax or the correctness of a social security number is choosing different shades of grey. Again the syntax can be checked with a regular expression, but to prove correctness a specific algorithm has to be executed, which is more costly. Let alone the necessity to know whether the bank account number actually exists. In most cases this would require executing some service or database query. Or in case of a URL, is a regular expression sufficient, or do we need to be sure the top level domain exists?
You could argue the wishfulness of expensive validations on value objects. Value objects are intended to be small and fast. This concept directs towards simple and cheap validations.
Once you have identified the way to check for valid values, you can build these into your value object. In most cases I would choose to create a separate method to check for validity. Thus, I can call this method from several locations in my value object, making sure I always check validity in the same way.
Tip Put your validation code in a separate method, so that it can be called from several locations in you code.
In the code example below for value object Isbn a static method called IsValidIsbn() does all the hard work. In this code example, this method is called from the constructor of Isbn. I’ve made the method public so it can be accessed from outside the Isbn value object.
private static Regex Expression =
new Regex(@"^(?=.{13}$)\d{1,5}(-)\d{1,7}\1\d{1,6}\1(\d|X)$");
public static bool IsValidIsbn(string newvalue)
{
return !Expression.IsMatch(newvalue);
}
public Isbn(string newvalue)
{
value = null;
if (string.IsNullOrEmpty(newvalue)) return;
if (IsValidIsbn(newvalue))
{
throw new FormatException(String.Format("{0} is no ISBN", newvalue));
}
value = newvalue;
}
Allow empty values?
An additional consideration in validating new instances of value objects should be whether or not null or empty values are allowed. Consider a class called Contact which has a property Email of type Email. This type Email is implemented as a value object. In this case it is worthwhile to investigate if contacts always have an email address. Probably not. So an empty or null value for Email should be allowable.
Tip Consider allowing empty values for your value objects. This empty value mostly refers to the case where a value is unknown, like with nullable types.
In the code example above for Isbn, an empty value is allowed. Moreover, by default the internal value is set to null, indicating that this instance of Isbn has not been set to a specific valid value, which is valid in this case. Passing an invalid value into the constructor however, is punished by throwing an exception. I usually employ this empty value to indicate that the ISBN is unknown for a particular book.
If you allow your value object to have null or empty values, you should also allow checking whether a specific instance is in fact null or empty. I usually implement a simple property called Empty on the value object to validate its emptyness.
public bool IsEmpty
{
get { return string.IsNullOrEmpty(value); }
}
Implement equality
By nature, value objects should be considered equal if the values of all their fields are considered equal. To find out, the default implementation of the Equals() method from Object uses reflection. On value objects this expensive algorithm can best be overloaded to provide a somewhat more efficient algorithm, like the one below from the Email value object.
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj is Email)
{
return Equals((Email) obj);
}
return false;
}
Additionally you can consider implementing the generic interface IEquatable
that handles equality checks for your specific value object. Implementing this interface will prevent boxing and unboxing when comparing two instances of your value object. Notice that the code for Equals()above uses the generic implementation to do the actual comparison.
Additionally, if you implement your value objects as a reference object, and two instances are also considered equal if they point to the same reference, it can’t hurt checking reference equality first. This will increase the performance of your framework.
public bool Equals(Email other)
{
return (value == other.value);
}
Consider also overloading the == and != operators on your value object when you implement IEquatable. Make sure that both implementations refer to the the generic Equals() method, to ensure that all implementations return the same results.
Tip Ensure that all equality validations in a value object exploit one single implementation.
By the way, do not overload only one of these operators. Overload neither or both.
public static bool operator ==(Email i, Email j)
{
return i.Equals(j);
}
public static bool operator !=(Email i, Email j)
{
return !i.Equals(j);
}
When implementing equality on your value object, there are some things to take into account:
- When you override Object.Equals() be sure also to override GetHashCode(). This is to guarantee that two instances that are considered equal also return the same hash code. In most cases it is sufficient to return the hash code of your internal value.
- Do not throw exceptions from these methods. Rather, if the arguments are null or of the wrong type have the methods simply return false.
- Do not name your interface members explicitly. This will cause boxing and unboxing when comparing instances of your value object. The same goes for the comparison interfaces described in the next paragraph.
Implement comparison?
Some value objects need to be sorted, others do not. For instance, I can imagine having to sort Money or PostalCode value objects, but sorting SocialSecurityNumber or Url objects is less likely. Although it is hard to be sure about not having to sort a list of value objects, in most cases you will be able to judge this from the context of the project.
Both the IComparable and the IComparable only define a single method called CompareTo(). Both methods return an int. the value of this number can be:
- Less than zero if the current object is smaller than the argument passed.
- Zero if the current objet and the argument passed are equal – for value objects this implies value equality.
- Greater than zero if the current object is considered bigger than the argument passed.
Under normal circumstances, implementing comparison on a value object comes down to capturing comparison on the internal value of your value object, like in the code example below for Email. Here, I’ve use the generic CompareTo() method to call comparison on the internal value value of Email. To shorten the path, I have included a check for null in the non-generic ComparoTo() method, even though value.CompareTo() is also check for null values.
public int CompareTo(object obj)
{
if (obj == null)
return 1;
if (!(obj is Email))
{
throw new ArgumentException("Argument is not a email address");
}
return CompareTo((Email) obj);
}
public int CompareTo(Email other)
{
return value.CompareTo(other.value);
}
If you decide that sorting is required on a certain value object, be aware of the possible consequences:
- Consider to overload the comparison operators, such as <, >, <= and >=. Again, these implementations cover internal value comparison, like the equality operators validate internal value equality. And again, make sure all of these exploit one single mechanism for comparing, in most cases the generic implementation of CompareTo().
- Do not name your interface members explicitly. This will cause boxing and unboxing when comparing instances of your value object.
Access your value object
In most cases, value objects are are used as arguments to method signatures, or perhaps as return values from methods. Because they tend to be implemented as struct, value objects are passed around by value, rather than by reference. Therefore the developer isn’t always in control on when new copies of his value object instances are created or manipulated. To avoid confusion it is good practice to make sure your value objects are immutable. That is, value objects do not contain public available members (either methods or properties) that can modify their internal value. Rather, instead of modifying it, developers get a new copy of the instance, with the new value.
Tip Make sure your value objects are immutable.
The .NET framework itself houses a very good example of this technique. Consider the DateTime value object. It has a great number of methods such as AddHours(), AddMinutes() and AddMonths() that at first glance appear to modify the internal value of DateTime. However, they do no such thing.
public DateTime AddMinutes(int months)
{
…
}
public DateTime AddMonths(int months)
{
…
}
Each of these accessor methods starts with disassembling the internal value of DateTime to facilitate calculation. After this simple math has been performed a new copy of DateTime is returned, that holds the newly calculated internal value.
Another example. One of the nice patterns applied in the .NET framework is called the TryParse pattern. This pattern is used on most base types and allows me to check whether an argument passed is of the specific type. A TryParse() takes an argument of the type that you would like to parse and tries to parse it to the particular value object. It will return either true or false to signify whether your attempt to parse has succeeded. If it does, the method passes back the parsed value as an output argument. Special about this pattern is that TryParse() is user-friendly. It does not throw an exception if parsing does not succeeed. It simply returns false.
DateTime start;
if (!DateTime.TryParse(txtContractStartDate, out start))
{
MessageBox.Show(“Value for start date is not a valid date”)
}
You might consider implementing one or more of these TryParse() methods for types that you often want to convert from. In most cases you will likely want to convert from string or any other base type. Implement it if trying to parse to your type occurs often enough that for performance reasons you want to avoid running into exceptions and exception handling that takes place when using ordinary Parse() methods.
Tip Consider implementing TryParse() methods on your value object to avoid exceptions.
A typical implementation for TryParse() is stated in the code example below for Isbn.
public static bool TryParse(string s, out Isbn result)
{
result = Empty;
if (string.IsNullOrEmpty(s))
{
return true;
}
if (!IsValidIsbn(s))
{
return false;
}
result = new Isbn(s);
return true;
}
Round up
Distuingishing between reference objects (represented by domain objects) and other types, such as values objects, but also enumerations and descriptors, help establish a much more stable and knowledgable model of your domain. Value objects in particular capture a significant part of all domain logic, especially when it comes to validating simple semantics and type safety. Implementing value objects delivers a number of additional questions, such as: do I implement it as a class or as a struct? How should I implement equality and comparison? How do I represent its internal value?
From my own personal experience I would advice you to guard your pragmatism when implementing value objects. Don’t overdo it. Keep your value objects slim and simple and avoid additional features as much as possible. A clear example would be comparsion and its accompanying operator overloads. Introducing mistakes herewith is far too easy. Only implement additional features if they are absolutely vital to your domain context. Moreover, try to realise different value objects similar to eachother. This allows you to create new value objects faster and minimizes your maintenance, for instance when new version of the .NET framework accommodate new techniques.
About the author
Sander Hoogendoorn
Principal Technology Officer Capgemini
In his role at Capgemini in the Netherlands, Sander is concerned with innovation of software development. He has coached many organisations and projects. Sander specialises in agile software development, software architecure and patterns, modeling (UML, DSL), model driven architecture and .NET. He is a well known speaker at international conferences and seminars. Sander has written numerous articles and columns and published two books, on UML and agile software development.
www.sanderhoogendoor.comwww.sanderhoogendoorn.com