Today’s article is an answer to a reader’s question about the use of Value Objects to represent the identity of an entity.
Value Objects and Identity: The Question
Here’s the question itself:
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:
Another way I could do this is to use my aggregate keys as a composite primary key:
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:
Which way do you think is the best and does it make sense using value objects for this?
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.
- 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:
it’s better to reference them like this:
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:
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:
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:
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:
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.
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:
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.