Using Value Objects to represent technical concerns



I was asked a question recently which I find quite interesting. It is actually a part of a code review request (which I’ll cover next week) but I decided to elevate it to its own blog post.

Using Value Objects to represent technical concerns

The question is this:

What do you think about Value Objects like Optional70LimitedString?

From the name of this Value Object, I assume that it represents any nullable string that should not exceed the 70 characters limit.

I answered a similar question in the article entitled When to create a Value Object. In it, I stated that the answer would depend on the specifics of one’s project and that the programmer should take into account the following variables when making such a decision:

  • The number and complexity of invariants.
  • The number of primitives the Value Object embodies.
  • Overall project complexity.
  • The number of duplications it will allow you to get rid of.

We could dissect the Value Object in question and say that there is only one invariant in that Value Object (the string should not be longer than 70 characters) and a single primitive underneath it (the string itself). So, judging by the first two parameters, you would probably pass on introducing it unless the project’s complexity or importance is really high or there just a lot of strings in the domain model that should not be longer than 70 characters. In all other cases, introducing such a Value Object wouldn’t justify the addition of a new class.

We could do all that but the answer would actually be irrelevant. When initially describing those variables, I made an implicit but very important assumption. I assumed that the Value Object represents a concept from the problem domain.

But does Optional70LimitedString really represent some domain concept?

It doesn’t. Being of 70 characters max is a technical concern that has nothing to do with the domain. It could be one of the invariants of a domain concept but it shouldn’t be treated as such in and of itself.

The problem with this type becomes obvious when you ask the following question: is this Value Object part of the ubiquitous language? In order for a class to be a genuine domain concept, the domain experts should use it when communicating between themselves and with programmers. And it’s not like a stakeholder would tell you:

Hey, we should probably check the optional 70 limited strings of each person that register with us, to make sure this is not a duplicate entry. Each customer must have a unique optional 70 limited string.

This sounds ridiculous. Value Objects are one of the most fundamental building blocks of your domain model, and so they should always tell a story about the problem you are solving. There’s no room for technical concerns here. The core parts of your domain model must speak the ubiquitous language.

So, to answer the original question of what I think about such a Value Object, I’d say this: I don’t like it. Optional70LimitedString doesn’t convey any domain knowledge; you cannot possibly know what this string is ought to represent.

And it’s not that different from a regular String if you think about it. You could use it to represent, say, a customer name, and you could also use it to represent a customer email. But does it really help you differentiate the two?

It doesn’t. Just as String allows you to do something like this:

Optional70LimitedString would allow you to do the same:

This assignment is meaningless, though, just as it would be meaningless to assign a Warehouse object to a Customer variable. Introducing a Value Object should help you reveal such issues.

Compare the code above to the following:

Now, it is not only obvious what those properties are about, it’s also impossible to mistake one for the other. The compiler wouldn’t allow you to do that.

What about the DRY principle?

So, don’t allow technical concerns to infiltrate the core parts of your domain model. If you want to introduce a Value Object, make sure you use the ubiquitous language of your domain.

Even if two domain concepts have similar invariants, don’t reuse the same Value Object. Either have separate Value Objects or stick to primitive types if those concepts are too simple to be worthy of distinct types. A Value Object like Optional70LimitedString is something that resides right in the middle between these two approaches. And it has the drawbacks of both without any of their benefits.

At this point, you might ask: but what about the DRY principle? What if we have two domain notions that, while being different from the domain’s point of view, still share the same invariants? Wouldn’t having two separate Value Objects entail code duplication?

It would. But it still wouldn’t contradict the DRY principle. Remember, DRY is not about code, it’s about domain knowledge. If two Value Objects happen to share the same invariants, it’s just a coincidence that has nothing to do with domain knowledge duplication.

Summary

  • When introducing a Value Object, make sure it represents a domain notion.
  • Don’t allow technical concerns to infiltrate the core parts of your domain model.
  • The maximum length of 70 characters is a technical concern and has nothing to do with the problem domain.

Related articles

Share




  • Michael Altmann

    Thanks for your fast and detailed answer. I have a few further questions:

    1. We discussed in our team if simple validations (like max length) is domain relevant or not. We decided that it is domain relevant. Because of that we put the validation in the Value Object. Is for example a ValueObject with simple validation inside (maxLength and trimming logic) for CustomerFirstName and a separate ValueObject for CustomerLastName with simple validation inside (maxLength and trimming logic) a acceptable solution in your opinion? (like in your code example 3.cs)

    2. If we don’t put technical concerns (=validation) in ValueObjects where can I place it? In the Application Layer (e.g. CommandHandlers)? The problem I see is that validation is spread in several layers. Technical concerns are checked somewhere and domain validations are executed in domain layer (in ValueObjects, Entities, …). If I have multiple consumers of my domain layer then I have to duplicate the validation logic of technical concerns.

    3. If a string property is required or not is for me domain relevant. How do you disign it because a normal string can also accept null?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      >We discussed in our team if simple validations (like max length) is domain relevant or not
      It is a domain concern and it is a good idea to keep this validation inside a Value Object. My point was only about the naming of those Value Objects. Instead of having a class like Optional70LimitedString, I recommend using specific concepts from the ubiquitous language, like Email, Article, and so on. Those Value Objects then can have all relevant validations baked in. The exampled you shared with me does all that. Again, my only concern is the name of the class. Instead of

      public Optional70LimitedString LastName { get; private set; }

      have

      public LastName LastName { get; private set; }

      • Michael Altmann

        Ah ok… Then it is clear for us. 🙂 Thanks for all…

  • Anderson GM

    Hey, Vladmir,

    Some people have the ability to explain ideas in a way the sounds obvious at the end. I think you have this ability too. Using the ubiquitous language to justify or not the use of some concept in your code made a lot of sense.

    Nowadays I think DDD is more related to businesses than code it self, because the code techniques could be seen as general good practices and we, as software developers, tend to put a lot of effort on the technical part and forgetting that the domain is the center of your application.

    Just one comment about the DRY, I like the idea that says you only have duplication in code when they do the same thing and change for the same reason – I believe it was said by Uncle Bob.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thank you!

      Indeed, DDD’s one of the most important objectives is to bring business and technical people closer together. Which is a great objective.

      If there are words “change” and “for the same reason” together, then it’s most definitely Uncle Bob 🙂

  • http://www.alireza-rahmani.ir/ alireza Rahmani khalili

    Hey
    Imagine in my model I have some const( I want to use them as enum) so is it right that I create valueObjects for consts? I heard create value object for const is wrong?