Always valid vs not always valid domain model



I’m back to the regular posting schedule. No more game development, at least for now.

Always valid domain models

The topic of always valid domain models is beaten to death already. There’s little doubt in the DDD camp that your domain model should be valid at all times. It’s much easier to program in an environment where you know that objects you operate reside in a valid state and you don’t need to worry about their internal consistency.

It also removes a lot of issues related to the human factor. When you have to keep in mind to always call the IsValid method before doing something meaningful with an entity, you are asking for a trouble. It’s too easy to omit such a call.

Always valid models help avoid unnecessary confusion too. Consider the following example:

Is it intentional that we validate the delivery entity in the first method but not in the second? Who knows. Maybe we don’t need it to be valid in order to postpone one’s delivery. In this case, the simple Contains check would suffice. Or maybe it’s a bug and the author just forgot to add this validation. You never know until you dig deeper. And even if you prove this code is correct, the requirements may change, and it would be really hard to track all cases that didn’t require a valid delivery object but now do.

So, it’s easier to keep your domain model valid at all times. And for that, you need to do the following:

  • Make the boundary of your domain model explicit.
  • Validate all external requests before they cross that boundary.

The first point means you should be clear which classes in your code base represent the domain model and which not. The second – that you need to perform all required validations upfront, before you delegate the work to domain classes.

Here’s a diagram that depicts this process:

Always valid domain model: Validation

Validation

In my experience, this whole dichotomy of always-valid and not-always-valid is actually an issue with the single responsibility principle.

As Greg Young pointed out in his old post, there are two types of validity: invariants and client input validation. People who argue in favor of the domain model being not always valid just try to make the domain classes both model the domain and hold data coming from the client input.

Indeed, when you have such code:

it’s hard to see how else you can validate the company’s name and phone number if not allowing them to enter a potentially invalid state first.

There’s a better alternative to this solution, though. You need to stop using the domain model to carry data that come from the outside world. Create a DTO instead and pass the data further to the Company domain class only after the validation is completed:

Alternatively – and that’s the approach I usually take – you can create Value Objects for notions of Name and Phone and put the validation logic there in a form of factory methods:

You still need the DTO but the validation logic is now located in domain model itself. Which is a preferable place for such logic.

So, again, separation of concerns and adherence to SRP makes wonders. Make the domain model a safe harbor, protect it from the external influence, and maintain its invariants.

What if invariants change?

But what if invariants change? Let’s say that the business people come to you and say that they now work with US companies exclusively and you need to reject companies with phone numbers outside the “+1” country region. And that they also want you to add a new mandatory field – NumberOfEmployees.

Now, all of the sudden, each company you have in your database has become invalid. That’s because you’ve strengthened the preconditions for the Company entity and thus broken its backward compatibility with the existing data.

So how to handle this situation? If you merely update the validation rules associated with the Phone value object, you will have an exception every time you try to work with an already existing company.

Does it mean you need to allow the domain model to become invalid? After all, how else you will be able to work with those companies?

One could argue that it breaks the whole always-valid paradigm but in reality, it is not. Strengthening an existing precondition like we did in the above example is a clear business case and should be treated as such. Meaning that you cannot simply outlaw all existing companies, you need to come up with a transition plan.

You, as a developer, need to work with the domain experts on how to make this happen. There’s no universal solution, each transition should be evaluated on a case-by-case basis.

So for the number of employees, maybe it would be enough to require this field for new companies. And for old, ask them to fill it out only when they make a change to their profile. Eventually, you will have this precondition consistent with all your data but there’s no way to impose it momentarily. Unless you are willing to purge your existing client base of course.

As for the US companies requirement, maybe you will send out a letter to those of them who reside outside the country and propose a migration plan. After some period of time, you will be able to remove them from your database. Or maybe not. In which case you will need to come up with a mechanism to distinguish between US and non-US companies in your domain model.

In any case, you can’t just strengthen the requirements, you have to come up with a transition plan and express this plan explicitly in your domain model.

Summary

  • Your domain model should be valid at all times.
  • For that, do the following:
    • Make the boundary of your domain model explicit.
    • Validate all external requests before they cross that boundary.
  • Don’t use domain classes to carry data that come from the outside world. Use DTOs for that purpose.
  • You cannot strengthen the invariants in your domain model as it would break backward compatibility with the existing data. You need to come up with a transition plan.

Related articles

Share




  • PA-WARE ASSISTENZA

    Hi

    I like always validation but use of DTOs is an extra well know topic…! and at least it require an extra effort in various cases.. ..first of all in the UI input scenario where sync the DTO’s exposed in the UI back with domain model it’s an extra annoyng stuff…

    I left dtos in my last project…!

    thanks for your suggestion
    (sorry for my english)

  • dave falkner

    I’ve thought of the Sporadically-Validated Domain Model Pattern as something like keeping clear plastic covers on your furniture, or a bra on your car or something. There’s a joke in there somewhere.

  • Daniel

    I’m wondering why you used a factory method to create a phone value object.

    Factory methods like one that creates a ‘Result’ from a string is something I try to avoid. When creating a value object from another type, I prefer argument checking and throwing exceptions in the public constructor of the value object type.

    From my experience, using factory methods that return ‘Result’ are incredibly annoying to use, if you use the same type as DTO (form outside into the domain model) and also as a value object type inside your domain model. Every time you want to access its value, you need to unpack it. (following a pure functional programming style this might be the way to go (bind operator), but I don’t think that functional programming helps us when creating a Phone value object)

    As an alternative, I could use the type with the factory (that creates Result) method as a DTO (which might enable the user to implement some way of result failure handling), and create another type to be used inside the domain model (which has public c’tor). But then I would use two different types to express the same thing which makes no sense for me.

    Why is there a factory method to create phone value object instance?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Having Value Objects act as Transfer Objects (DTOs) is something I advocate against. A DTO doesn’t serve the same purpose as a Value Object. The former is not part of the domain model while the latter is. More about the difference between the two: http://enterprisecraftsmanship.com/2015/04/13/dto-vs-value-object-vs-poco/

      When entering the domain model (i.e. transforming data from a DTO into a Value Object) a validation is needed to make sure the domain model always resides in a valid state. A factory method in Phone is useful because it allows you to hide the constructor so that no client can create a Phone instance directly, only through the factory method and only after validating the string. Here’s a full example of an Email value object: https://github.com/vkhorikov/FuntionalPrinciplesCsharp/blob/master/New/CustomerManagement.Logic/Model/Email.cs

      • Daniel

        There are two discussion points now 😉

        First
        Why should a value object not serve as a DTO in order to pass data from outside of the model into the inside?

        Second
        If you validate data in the c’tor and throw an exception in case of invalid data, then an invalid instance can’t be created. Why do you favor the approach with the factory method?

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          1) Couple reasons.

          1.1) DTOs represent data contracts between applications. Once published, data contracts are hard to change because of backward compatibility issues, so it’s often a requirement to keep the shape of the data unchanged. DTOs help decouple the domain model from those data contracts. You can freely change the domain model (Value Objects are part of it) as long as you keep the DTOs unmodified, changing only mappings between them.

          1.2) You’d need to open Value Object’s setters in order to enable de-serialization which damages encapsulation.

          2) It’s fine to throw exceptions in case of invalid data provided that you don’t use them later to control the program flow. Exceptions are for unexpected situations only (such as violation of class’s invariants)

          • Harry McIntyre

            This is a very timely conversation – I’ve just been working on a new ValueObject library (https://github.com/mcintyre321/ValueOf). Some feedback from both of you would be appreciated.

            Currently I have a `static TValueObject From(TValue v)` method which throws an exception when validation fails. I was thinking of having a second method `static OneOf<TValueObject, ParseError> Parse(TValue v)`.

            I was thinking I would build a series of framework specific ModelBinders/Serializers etc. to allow you to use `ValueOf`s in contracts.

          • http://enterprisecraftsmanship.com/ Vladimir Khorikov

            I see a pattern in how you name your NuGet libraries 🙂 And a really nice idea!

            You definitely need a version of the method that returns a Result-like object, not just throws an exception for cases where the client wants to create a value object manually. Normally, I would say that you don’t need a version of this same method throwing exceptions at all but provided that you will use it for ASP.NET binders, there’s no way around this.

  • Dave

    So what if your validation isn’t as simple as just checking if a phone number or email address is in a correct format. What if you need to go to your database first? For example, check if an email address already exists.

    Where and how do you do that type of validation?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      This should be outside the domain model as it touches the external service – the DB. The best to put such a validation to is the application services layer.