C# – When is primitive obsession not a code smell

abstractioncdomain-driven-designdomain-modelmodeling

I have read plenty of articles recently that describe primitive obsession as a code smell.

There are two benefits of avoiding primitive obsession:

  1. It makes the domain model more explicit. For example, I can talk to a business analyst about a Post Code instead of a string that contains a post code.

  2. All the validation is in one place instead of across the application.

There are plenty of articles out there that describe when it is a code smell. For example, I can see the benefit of removing primitive obsession for a post code like this:

public class Address
{
    public ZipCode ZipCode { get; set; }
}

Here is the constructor of the ZipCode:

public ZipCode(string value)
    {
        // Perform regex matching to verify XXXXX or XXXXX-XXXX format
        _value = value;
    }

You would be breaking the DRY principle putting that validation logic everywhere a zip code is used.

However, what about the following objects:

  1. Date of Birth: Check that greater than mindate and less than today's date.

  2. Salary: Check that greater than or equal to zero.

Would you create a DateOfBirth object and a Salary object? The benefit is that you can talk about them when describing the domain model. However, is this a case of overengineering as there is not a lot of validation. Is there a rule that describes when and when not to remove primitive obsession or should you always do it if possible?

I guess I could create a type alias instead of a class, which would help with point one above.

Best Answer

Primitive Obsession is using primitive data types to represent domain ideas.

The opposite would be "domain modeling", or perhaps "over engineering".

Would you create a DateOfBirth object and a Salary object?

Introducing a Salary object can be a good idea for the following reason: numbers rarely stand alone in the domain model, they almost always have a dimension and a unit. We don't normally model anything useful if we add a length to a time or a mass, and we seldom get good results when we mix meters and feet.

As for DateOfBirth, probably -- there are two issues to consider. First, creating a non-primitive Date gives you a place to center all of the weird concerns around date math. Many languages provide one out of the box; DateTime, java.util.Date. These are domain agnostic implementations of dates, but they are not primitives.

Second, DateOfBirth isn't really a date time; here in the US, "date of birth" is a cultural construct / legal fiction. We tend to measure date of birth from the local date of a persons birth; Bob, born in California, might have an "earlier" birth date than Alice, born in New York, even though he is the younger of the two.

Is there a rule that describes when and when not to remove primitive obsession or should you always do it if possible.

Certainly not always; at the boundaries, applications are not object oriented. It's fairly common to see primitives used to describe the behaviors in tests.

Related Topic