This is a question that seems to surface over and over again when I talk to fellow developers, and while it is somewhat clear to many what the difference between entities and value objects are, it seems it is not clear when to actually use an entity and when to use a value object. For some reason, people also seem to favor entities over value objects when modeling, when it really should be the other way around. If you are working with only entities, then my friend, it sadly seems you are not alone out there!
Why is this? And why are people so reluctant to implement and use value objects?
I figured I would try to clear up the misconceptions a bit, and provide two examples that I often use to exemplify the difference between entities and value objects. Both of them are insights taught by Eric Evans during his DDD Immersion class, which I attended in Stockholm about a year ago. They are slightly modified as I don’t remember the exact phrases used by Eric. I also extended, and twisted each of the examples.
In domain driven design, context is everything, and it is also a key factor when choosing between modeling an object as either an entity or a value object. A context (or bounded context to be more specific) has explicit defined borders and is in itself an application of its own. Within this context we define a model, create our ubiquitous language and implement behavior among other things to fulfill our requirements.
The main thing here is to understand that a context’s model, is an abstracted view of either your, or your customer’s business, and will not solve all the problems of a domain. An object in one context might be something completely different in another context, even though both contexts are within the same domain, and the object has the same name. It all depends on which abstraction your requirements emphasize, and is also the reason why two customers operating in the same domain, may have two completely different models. In one of those models, an object might be a value object, whereas in the other model it is an entity.
The – $100 dollar bill! – example
You and your colleague are each holding one of your own $100 bills in your hands, and there isn’t anything wrong with them or anything like that. If I asked you to swap those two bills, none of you would really care, swap them, be none the wiser, and move along like nothing had happened. After all, neither of you earned nor lost money! A $100 bill is a $100 bill, and you would most likely only compare the number of zeros printed on the bill. After you compared that value you wouldn’t really care which one you hold in your hand, and since we do not care about instance, and we compare them by state, we are talking about typical value objects.
But, doesn’t a $100 bill have an identification number? Well, actually it does, and that means that in some context, the exact same bill you are holding in your hand is very important to somebody, in their context. And, in that context it might actually be an entity. However, even though the $100 bill has an identification number and we may think that it’s an entity, it is not necessarily so. As always, it depends on the context.
With the same context as above, how many of you would swap your credit cards, if each card had a $100 balance on it?
The – A glass of water! – example
Imagine you and I just sat down to have a meeting. I poor two glasses of water, and then give you the opportunity to pick one of the glasses. You would not really care which one of the glasses you would pick, since it is just two glasses on a desk. So far, in this context, both glasses are the same, and its equality would be determined by the type of glass, amount of water, and perhaps the quality of the water. Since we do not care about instance, and we compare by state, we are talking about typical value objects once again.
Now, let’s redo the same scenario, but as I poor the two glasses of water, I take a sip from one of the glasses. Most people (I don’t know about you though) would by default pick the glass I have not taken a sip from, as the other glass would have immediately become my glass of water. At this point, the type of glass, amount of water and quality is no longer of any concern, because in this new context I have, by the sip I took, polluted one glass as mine, thus imposed it with an identity. So as this context has evolved, both glasses of water are now entities, even though I only sipped from one of them.
You could argue that the glass of water is still a value object, but now attached to a person object. But, I didn’t swallow a glass, I drank from the contents of a glass. That content might in itself be a value object, which was temporarily attached to the glass, but is now attached to the person object. So now we separated the glass of water into two objects, and now we have to reevaluate the whole scenario as our context evolved.
For fun, let’s back up and twist this -sip to impose identity- example further, and I will generalize a bit here (sorry all smokers). Smokers often ask for the butt of a cigarette if they are temporarily out of smokes. They don’t mind putting a cigarette to their mouth that someone else already smoked on, but at the same time they wouldn’t share a glass of water with the same person. So by taking a sip from a cigarette, we may still be using value objects.
What we changed here is the object we interact with, and by changing from a glass of water to a cigarette we also changed an attribute of the context. It might be that we are in two completely different contexts, but if we are in the same context, both a cigarette and a glass of water would most likely not inherit from a shared abstraction like, Product, as they would be modeled and implemented quite differently. Watch out for those generalized abstractions, as they will most likely impede you reaching a supple design.
Consider the context above and these method signatures, which would you prefer? These,
void Drink(IDrink drink);
void Smoke(ICigarette cigarette);
void Sip(IProduct product);
Again, it comes down to our context and how we want to implement our behavior. And while we are on the subject of behavior, we actually changed a few behaviors in the model, without really mentioning it, but the most important change to the scenario above is that we can no longer assume that, taking a sip from something, will automatically be okay by a person object.
Those examples didn’t help me at all
Here are some information and a few tips about value objects that you might want to use as guidance.
Always understand the context you are in, listen to the language and the concepts that your domain experts talk about. Without a clear perception of your context, you are much more likely to fail before you even get started.
- Identity through state
A value objects identity is based on its state. Two value objects with the same property values would be considered the same object.
Value objects are immutable objects, which means you cannot change a value object’s state without replacing the whole object. Immutable objects are always easier to handle and understand, and since immutable objects are inherently thread-safe, they are a better choice for today’s multi-threading applications.
Value objects are throwaway objects. During a transaction you may fetch one, and then by using the Side-Effect Free pattern, which is especially useful when dealing with value objects, create n-objects before reaching your final instance, which is the one that will be persisted.
- Explicit identity
Even though the object you are trying to model has an explicit identity, do not get fooled into making it an entity just because it has this identity. Does the identity mean anything in your context?
- Temporary name
At times when you are unsure of what an object is and what it should do, it can help to give your object some random name that doesn’t mean anything. This will allow you, and people around you, to not get stuck on a particular concept, and hopefully you will be able to continue refining your model. This will also help you to stick with a value object implementation as long as possible.
When you need to refactor your model, it will always be easier to make an entity out of a value object, than the other way around.
Still in doubt?
When in doubt, and you wonder what an object is, and how it should be modeled, always favor value objects. I will let Bart repeat it a few times on his chalkboard, and perhaps you will remember this chant, and think twice the next time you are about to add another entity to your model.