This post does not contain unique ideas, this is just my understanding of a good video I recently found on youtube. You can scroll down to find a link to the original video talk on this topic.
Let's talk about primitive obsession. So primitive obsession is when you are using primitives to represent domain concepts. We'll talk about examples, but let's first talk about it.
So the definition said that primitive obsession is when you are using primitives to represent the main concepts. So let's exemplify. Let's say that in our application for some reason we want to have ages. So we want to be able to represent the age of a person and for the sake of the example it's actually important that it's an alive person. If we would represent the age with a common integer then we are exercising primitive obsession or exhibiting primitive obsession. We are being obsessed with primitives. Think about it this way. What is the domain of possible values for integers and what is the domain of possible values for age? If we use int to represent ages then suddenly anything that is a valid integer seems to be a valid age. But that's not actually true because an integer could be minus something, but it makes no sense for an age to be minus something. If that sounds confusing I highly recommend that you check out the Liskov substitution principle. It's very closely related to primitive obsession problem. In our case age should be a range of possible values, and I could have age as a class. So something that I find particularly interesting about primitive obsession is that it's not immediately obvious that primitive obsession is a bad thing. Before I first learned about primitive obsession I thought that making use of primitives or prioritizing the use of primitives over complex objects is a good thing simply because primitives have less dependencies if you think about it right. I mean depending on a primitive, having a method that depends on a string feels less heavy than having a method that depends on some kind of complex object that you've built yourself. If you're accepting something of a type then you need to know about the existence of that type. But when you're depending on a particular class, you may in fact be depending on a whole series of classes or tree of classes. So again from a purely theoretical point of view from a purely intuitive point of view it seems that depending on a primitive is actually a good choice because you are reducing these dependencies. You're eliminating the possibility of depending on trees of complex objects but as you may have guessed of course this is not the full story. So here's the problem. The problem is that by coupling to a primitive rather than a complex object you haven't actually eliminated dependency, you have hidden the dependency, you have made the dependency implicit rather than explicit. So the dependency still exists but it's not immediately obvious to other developers or to yourself in some period of time whether at any given point in time the dependency is met or not, whether the constraints are satisfied or not. So if you think about a typed language for example you can't know whether you've satisfied the dependencies from a type perspective. Because as long as you pass it a string or an int or whatever you chose in that scenario, the compiler or the interpreter would say that it's okay. But it could of be that your string or int in this particular scenario wasn't actually a valid value and this is where the key lies. So again think about the domain of possible values or the range of values that are allowed for an object or for an instance of a type at any given time.
In the previous example we expect ages of people that are alive. If you model age as an int then any number that is a valid int should be a valid age. But this is falsy because domain says that ages in this system in this particular location can only be positive and say below 100. We have constraints, we have a range of allowed values. That means that all integers that are outside this boundary, they aren't actually valid ages. But they are valid integers so in other words integers are not a valid proxy in this case for ages.
So, using primitives for domain specific fields actually means deferring a need to strengthen your types in future. Every time while choosing a type for domain specific field I suggest to ask yourself following question: Will it be possible to have any constraints on this field? Yes answer means that most probably you have to use complex type.
Click to view an original video explanation for Primitive Obsession