Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
281 lines (199 sloc) 10.3 KB
title date draft tags slug aliases disqus_identifier disqus_title
Value Objects and Identity
Thu, 10 May 2018 11:52:00 +0000
false
Entity
Value Object
value-objects-identity
/2018/05/10/value-objects-identity/
Value Objects and Identity

Today’s article is an answer to a reader’s question about the use of Value Objects to represent the identity of an entity.

<!--more-→

Value Objects and Identity: The Question

Here’s the question itself:

Start of quotation.

I’m understanding the core concepts of what a value object is such as immutability and equality and it occurs to me that I could represent the identity value of an entity using a value object.

I have an aggregate root called PlanningTool and it has many PlanningToolTabs and then that has many PlanningToolGroup.

My first design is on each of those entities I put an int Id property that Entity Framework maps in the database and is the primary key that is generated by the database:

{{< highlight csharp >}} public class PlanningTool { internal int Id { get; private set; }

    // Rest of class
    public ICollection<PlanningToolTab> Tabs { get; private set; }
}

public class PlanningToolTab { internal int Id { get; private set; }

public int PlanningToolId { get; private set; }
public int PlanningToolGroupId { get; private set; }
    // Rest of class
    public ICollection<PlanningToolGroup> Groups { get; private set; }
}

public class PlanningToolGroup { internal int Id { get; private set; } // Rest of class } {{< / highlight >}}

Another way I could do this is to use my aggregate keys as a composite primary key:

{{< highlight csharp >}} public class PlanningTool { internal int Id { get; private set; }

    // Rest of class
    public ICollection<PlanningToolTab> Tabs { get; private set; }
}

public class PlanningToolTab { public int PlanningToolId { get; private set; } // Part of the primary key public int PlanningToolGroupId { get; private set; } // Part of the primary key

    // Rest of class
    public ICollection<PlanningToolGroup> Groups { get; private set; }
}

public class PlanningToolGroup { internal int Id { get; private set; } // Rest of class } {{< / highlight >}}

Then the other way I could do this is I could model the identity values as value objects and the value objects would then become nested like this:

{{< highlight csharp >}} public class PlanningTool { internal PlanningToolId Id { get; private set; }

    // Rest of class
    public ICollection<PlanningToolTab> Tabs { get; private set; }
}

public class PlanningToolTab { public PlanningToolTabId Id { get; private set; }

    // Rest of class
    public ICollection<PlanningToolGroup> Groups { get; private set; }
}

public class PlanningToolGroup { internal PlanningToolGroupId Id { get; private set; } // Rest of class }

public class PlanningToolTabId : ValueObject { public PlanningToolId PlanningToolId { get; } public PlanningToolGroupId PlanningToolGroupId { get; } // Equality members }

public class PlanningToolId : ValueObject { public int Value { get; } // Equality members }

public class PlanningToolGroupId : ValueObject { public int Value { get; } // Equality members } {{< / highlight >}}

Which way do you think is the best and does it make sense using value objects for this?

End of quotation.

Two Types of Identities

Before answering the direct question, let me step back and discuss the two types of identities. Or rather, the two ways they are used.

They are:

  • The entity’s own Id, and

  • The Id with which this entity references other entities.

You need to differentiate between the two. Entity’s own Id represents its inherent identity which we use to distinguish it from other instances of the same class. At the same time, it’s an internal implementation detail which other entities shouldn’t be aware of.

And so, it’s OK to use an Id for Case #1, but not for Case #2. When the entity references another entity, it shouldn’t know how that other entity represents its identity, this concern is ought to be encapsulated away. In other words, the entity can and should know about its own Id, but shouldn’t be aware of the Ids of other entities.

To read more about it, see Link to an aggregate: reference or Id?.

The corollary from this guidelines is this. If you have a many-to-one relation between entities in a single bounded context, it is better not to reference them by Id and instead use a direct link.

So instead of this:

{{< highlight csharp >}} public int PlanningToolId { get; private set; } {{< / highlight >}}

it’s better to reference them like this:

{{< highlight csharp >}} public PlanningTool PlanningTool { get; private set; } {{< / highlight >}}

And it makes sense if you look at it from the domain modeling perspective. Surrogate Ids like those generated by the database have no place in the ubiquitous language. When you are talking about a use case, you don’t describe it as:

"All customers of our organization have a dedicated manager Id assigned to them".

If you do, the person you talk to would probably have a similar look:

Wat?
Wat?

When talking about a manager, you refer to it as that - a manager. No need to bring up these silly implementation details. And when it comes to Domain-Driven Design, one of the most important practices there is to align your code base along the lines of the ubiquitous language. At least when working on the core domain.

Of course, it’s not always feasible to entirely avoid the use of Ids when referencing other entities. After all, there are technical limitations to what we can do when modeling the domain. But still, this practice is applied not nearly as much as it should.

And to be frank, technical limitations is only part of the problem. The other part is that we, programmers, are so accustomed to Ids that we rarely even notice the disconnect with the real world. The good news is that this second part is fixable, which I’m trying to do here :)

Value Objects and Identity: The Answer

Alright, that was quite a digression. Let’s get back to the reader’s question. What is the best way to represent an entity Id? Note that we are talking about the entities' own Ids here, not the Ids with which it references other entities.

In most cases, option #1 would be enough. Here it is again:

{{< highlight csharp >}} public class PlanningTool { internal int Id { get; private set; }

    // Rest of class
    public ICollection<PlanningToolTab> Tabs { get; private set; }
}

public class PlanningToolTab { internal int Id { get; private set; }

public int PlanningToolId { get; private set; }
public int PlanningToolGroupId { get; private set; }
    // Rest of class
    public ICollection<PlanningToolGroup> Groups { get; private set; }
}

public class PlanningToolGroup { internal int Id { get; private set; } // Rest of class } {{< / highlight >}}

A nice benefit of this approach is that the presence of Id in each and every entity allows you to factor it and the comparison logic out to a base class, which I highly recommend doing. It would be even better if you could replace the following Ids with the respective entity references:

{{< highlight csharp >}} public int PlanningToolId { get; private set; } public int PlanningToolGroupId { get; private set; } public PlanningTool PlanningTool { get; private set; } public PlanningToolGroup PlanningToolGroup { get; private set; } {{< / highlight >}}

But let’s assume this is not an option for some reason.

The second option is not as good as the first one. Here it is once again:

{{< highlight csharp >}} public class PlanningTool { internal int Id { get; private set; }

    // Rest of class
    public ICollection<PlanningToolTab> Tabs { get; private set; }
}

public class PlanningToolTab { public int PlanningToolId { get; private set; } // Part of the primary key public int PlanningToolGroupId { get; private set; } // Part of the primary key

    // Rest of class
    public ICollection<PlanningToolGroup> Groups { get; private set; }
}

public class PlanningToolGroup { internal int Id { get; private set; } // Rest of class } {{< / highlight >}}

Sure, we save ourselves additional field both in the code and in the database as we are getting rid of PlanningToolTab.Id but you lose this nice bonus of having a single base entity class.

Another drawback here is that ORMs generally don’t deal with composite primary keys very well. And so, I wouldn’t recommend this option.

The third one is quite elegant as it builds up upon functional programming principles. It avoids the primitive obsession by introducing a separate class for each Id in the domain model.

I have mixed fillings about this one, though. On one hand, it allows you to avoid the drawbacks that come with the primitive obsession, such as mistaking Id of one entity with the Id of another. If you try to undertake this operation:

{{< highlight csharp >}} customer.Id = order.Id; {{< / highlight >}}

the compiler will point its meaninglessness out by raising a compilation error and will be absolutely right to do so.

On the other hand, the good intentions are misplaced here. You shouldn’t deal with entities' Ids directly anyway. In most cases, it’s a violation of the encapsulation principles (also known as Tell Don’t Ask). And so, there’s no need to create Value Objects around the identifiers. Hide them from the eyes of the client code, and you’ll be fine.

Therefore, I wouldn’t recommend Option #3 either. Go with the first one, it provides the best combination of simplicity and usefulness.

You can’t perform that action at this time.