I’ve received an interesting question that I thought I would share with you. It’s about the article I wrote some time back: Nulls in Value Objects.

1. Preamble

The gist of this article is the choice between ValueObject<Nullable<T>> and Nullable<ValueObject<T>>:

You should prefer ValueObject<Nullable<T>> if the null (or empty) value is valid for the given value object. Otherwise, you should go with Nullable<ValueObject<T>>.

For example, a phone number can’t be an empty string. Such a string would violate PhoneNumber's invariants, which are (most likely) the requirement to contain exactly 10 digits.

And so, the nullability should be pushed from PhoneNumber to Customer:

public class Customer : Entity
{
    public Maybe<PhoneNumber> Phone { get; }
}

On the other hand, something like OrderQuantity is well-suited for the ValueObject<Nullable<T>> approach, because 0 is a perfectly valid value for the order quantity.

2. Domain-Specific Nulls

Now, to the question itself:

I have a question regarding the representation of null. When something is null it usually (maybe always) means something, e.g. a date being null might mean infinite, a phone number being null might mean its not provided yet, etc.

Instead of having nulls and leaving it up to the developer to figure out why its null maybe we should be more explicit?

For example, instead of having customer’s phone number nullable as below:

class Customer
{
    Maybe<PhoneNumber> PhoneNumber { get; set; }
}

class Employee
{
    PhoneNumber PhoneNumber { get; set; }
}

What do you think about having 2 separate value objects?

  • CustomerPhoneNumber — This one exposes a property that clearly defines what null means in this context, e.g. CustomerPhoneNumber.NotProvided which internally sets the value to null.

  • EmployeePhoneNumber — This one does not allow its internal field to be null or empty.

This is a great question, and it ties directly to the topic of ubiquitous language (one of the main principles in DDD) — the use of proper domain terms when writing code.

Indeed, the introduction of 2 separate value objects (CustomerPhoneNumber and EmployeePhoneNumber) brings about better readability because we can now clearly see how the nullability requirement differs for these two use cases.

However, you need to assess both ends of the deal. The downside of this approach is that the code base becomes more complex due to the additional classes.

Is this cost worth the readability?

The answer depends on the particular use case.

In the above example with the phone numbers, I would stick to Maybe because it’s pretty easy to interpret the meaning of Maybe<PhoneNumber> there, and so the additional class isn’t worth it.

But for any use case other than "value not provided", I do recommend that you make the null domain-specific.

For example, if null in the expiration date means that that date is infinite, then you are better off wrapping that null into a separate value object.

Otherwise, it wouldn’t be easy to deduce the meaning of null and you may conflate it with the usual "value not provided" situation.

--Vlad

https://enterprisecraftsmanship.com


Enjoy this message? Here are more things you might like:

Workshops — I offer a 2-day workshop for organizations on Domain-Driven Design and Unit Testing. Reply to this email to discuss.

Unit Testing Principles, Patterns and Practices — A book for people who already have some experience with unit testing and want to bring their skills to the next level.
Learn more »

My Pluralsight courses — The topics include Unit Testing, Domain-Driven Design, and more.
Learn more »