Sitemap
iamprovidence

iamprovidence

👨🏼‍💻 Full Stack Dev writing about software architecture, patterns and other programming stuff https://www.buymeacoffee.com/iamprovidence

Validation in DDD

4 min readSep 30, 2022

--

Validation is something that is performed across your entire system. Your input fields have some validation, then you validate your request, your domain model has its own set of rules, and even your infrastructure, like the database, will perform some validation, probably on uniqueness or something. Validation does happen a lot.

When coming to DDD, there are two common opinions about having a domain model (not classes) in Always-valid state and Not-so-valid 😁 Today we will see a difference between those approaches and decide which one is better.

However, before talking about validation, it is worth mentioning Business invariants. You may have heard about it, but just to make sure we are on the same page, let’s see:

Business invariant — is a condition that should be truthy for the entire lifecycle of domain model.

In human language, if your business requirement says it should not be possible for Order to have more than three items in it, then it should not be possible to assign more than three items to Order class in your code.

Long story short, business invariant and validation are the same thing. While validation is a more general term, a business invariant is a validation rule related to your business model. Since we are talking about DDD, especially focusing on Domain, just think about those as they are equal.

Now, when everything is clear, shall we begin?

Always-valid model

Adherents of the Always-valid school claim that the domain model should always be in a valid state. Meaning, before performing any action, validation should occur. Notice how in the example below, we throw an exception for invalid arguments or when an invariant is violated.

That is not a new thing in programming and is being overused so often that developers start confusing what terms you should use to describe it. Some refer to it as if-then-throw pattern, others fail-fast principle, early return, and so on. Whatever you call it, it’s still the same thing.

Get iamprovidence’s stories in your inbox

Join Medium for free to get updates from this writer.

However, the simple use of an exception might not ensure that the object is in a valid state. Some complex business validation will require interaction with the database. Which is expected. Domain rules can not be fully validated by a single entity, and most likely you still will need to come up with some service or another solution.

Not-always-valid model

Following this approach, we allow our entity to be invalid. Instead of always keeping it in a valid state, we will perform validation of all invariants in a single Validate() method:

The Validate() method is usually called before persisting the domain model in the repository, but can also be used before performing complex business logic:

The advantage here is that we gather most of the validation in a single place. Why most? — you may wonder. Because there are still some rules that just can not be validated on a final state, like the one we had in Cancel() method.

Secondly, it is also possible to accumulate validation messages in a single exception. With fail-first approach, you would need to send tons of requests before you finally configure each property to be correct. On the other hand, this one will return all validation messages together.

(By the way, you can follow the Always-valid model by calling the Validate() method in each action, having all the validation logic encapsulated in a single place).

Which one is better? 🤔

Now, when you have seen different validation approaches, we can decide which one to use. Even though it is up to you, my suggestion would be to use the Always-valid approach.

With not-always-valid approach, you simply can not be sure that you have a fully working model. Thus, there is always a need to investigate all references to make sure validation is not missed. That just tons of maintenance overhead.

Although it may be useful to ignore the validation rule in some cases (when writing a unit test, for example, it always annoys me that I need to spend hours arranging correct data, for my test to work) I better catch an exception then store invalid data because I forget to call Validate().

On the other hand, always-valid approach decreases the complexity of code support. It does not allow violating any business invariants. It does not allow providing wrong arguments, which leads to an unstable state of your model. And you should not even do that. You should not have an empty address, and if something like that happens, it is a rather exceptional situation that should be reported right away and not in another function after another hundred lines of code.

Choose Always-valid approach, and you will see how much easier it is to maintain your codebase.

📝 Let me know in the comments which one you prefer and why

☕️ Buy me a coffee if you want to support me

✅ Follow to get more articles about DDD

--

--

iamprovidence
iamprovidence

Written by iamprovidence

👨🏼‍💻 Full Stack Dev writing about software architecture, patterns and other programming stuff https://www.buymeacoffee.com/iamprovidence