Primitive obsession is a known code smell that describes the usage of primitives for representing domain values. For example:
In this class both the user’s name and age are being represented by a string and an int respectively.
How else could we represent a name and an age?
Well, the primitives are the correct ones but not for direct usage. We can build value objects upon them and encapsulate both the meaning of the value and the business logic:
So what? You just wrapped a primitive and moved the check.
True but now we have:
1. A reusable object
When the time comes and we need to have a new named entity we just use the class above and we can be sure that the business logic will follow along and be concise in the entire project.
2. Always valid instances
Whenever we see an instance of name or age we are certain that the instance holds a valid value. This means that an entity that consists from those value objects can only create valid instances and this means that the code that uses those entities (and value objects) does not need to have unnecessary checks. Cleaner code.
3. Code that scales more easily
Lets say that our business logic changes and we need to support users with invalid names too but without the need to deal with the name itself.
Having a value object can help implement the change easily. We just change the
Name class. All other code remains the same:
4. Code that is more robust
Lets say that our entities have the notion of an id and that after a few years the underline value needs to change from an integer to a long.
By having a value object to represent the
Id all changes will take place in the outer layers of our architecture where we load/fetch ids from databases/network and create the id instances. The rest of project will remain untouched, especially the domain layer that holds our business logic.
If we had chosen the path of having an integer property in every entity then all of our entities, and the code that uses them, would need to change too.
5. Code that is more readable and reveals its usage
6. A blueprint of our domain
When we open our project and see files like Invoice, Price, Quantity, Amount, Currency we get an immediate feel of what this project/package deals with and what are its building blocks.
We consume information that we would otherwise need to dig inside each file to find out.
7. A context
The final and most important part of having value objects is that now we can complete our entities and build a context for our domain. A common language that we can use to communicate with other engineers and stake holders in general.
Primitives are essential and they are the building blocks of a language but not of our business. The rest of company does not build its workflows upon integers and strings. It uses the businesses’ building blocks like age, name etc. It is vital that we do the same too in order to keep our code base in sync with the business.