4

I recently asked this question: Validation inside Constructor

I am trying to decide where to put the validation in a DDD app. I believe it should be done at every layer.

I am now concentrating on the Domain model. I was expecting validation to go in the Setter methods like this: https://lostechies.com/jimmybogard/2016/04/29/validation-inside-or-outside-entities/

What is the "best" way to do validation. I am talking from the perspective of a DDD purist. I realise that the "best" way may not always be the most practical way in every situation.

Also, I am not necessarily saying that DDD is always the best approach to solve a problem. I am just trying to imporve my thinking in this specific area.

I believe the options are:

1) Validation inside setters as described here: https://lostechies.com/jimmybogard/2016/04/29/validation-inside-or-outside-entities/

2) IsValid method as described here: http://enterprisecraftsmanship.com/2016/09/13/validation-and-ddd/

3) Check validity in application services layer as described here: http://enterprisecraftsmanship.com/2016/09/13/validation-and-ddd/

4) TryExecute pattern as described here: http://enterprisecraftsmanship.com/2016/09/13/validation-and-ddd/

5) Execute/CanExecute pattern as described here: http://enterprisecraftsmanship.com/2016/09/13/validation-and-ddd/

I am using NHibernate so the setters on the domain objects must have a visibility of: protected set.

  • One of the nice things about encapsulation is that there is no need to validate at every layer. You can't check what you can't touch. – candied_orange Oct 14 '17 at 23:08
  • Just a quick security note: Whatever validation you do, make sure it's redone at the server. Make absolutely sure the server version is correct. It's easier to perform one bad network request and display an error to the client based on the response code than it is to filter through all of your databases to look through bad data because someone (either maliciously or stupidly) broke or worked around the client-side checks. – Nic Hartley Oct 20 '17 at 16:29
9

It's an interesting question that seems to come up in a variety of guises.

I am of the opinion that the best approach is to allow the concept of an object that is a invalid state.

The reason being that the validation rules for an object are usually not set in stone. They may change over time, or be different for different operations. Thus an Object which you created, populated and persisted some time ago, may now be deemed invalid.

If you have setter or constructor validation checks, then you have a big problem in that your application will error when you try to retrieve these entities from your database, or reprocess old inputs etc.

Additionally I don't think that business rules incorporate simple yes/no validation for the most part. If your domain is selling cakes and you don't deliver south of the river, but someone offers you a million pounds to do it. Then you make a special exception.

If you are processing millions of applications and you have strict rules about what characters can be in a field, then you probably have a process for correcting bad fields. You don't want to be unable to accept a bad field at all, it just follows a different path through the Domain.

So, if in the code you are so strict that 'invalid' data can just never exist because the constructor would throw an exception, you are bound to be brittle and fail for questions like "how many people filled the form in wrong?"

Allow the data and fail the operation. This way you can adjust the data or rules for the operation and re run it.

example:

public class Order
{
    public string Id {get;set;}
    public string Address {get;set;}
    public void Deliver()
    {
        //check address is valid for delivery
        if(String.IsNullOrWhiteSpace(this.Address))
        {
            throw new Exception("Address not supplied");
        }

        //delivery code
    }
}

So here we are unable or don't wish to deliver to blank addresses. The Domain object Order, allows you to populate a blank address but will throw a exception if you attempt to deliver that order.

An application, say a queue worker processing orders from json data stored in a queue, coming across an 'invalid' order:

{
    "Address"  :""
}

Is able to create the Order object, as there are no validation checks in the constructor, or the setter for Address. However, when the Deliver method is called it will throw the exception and the application will be able to take an action. eg

public class QueueWorker
{
    public void ProcessOrder(Order o)
    {
        try
        {
            o.Deliver();
        }
        catch(Exception ex)
        {
            Logger.Log("unable to deliver order:${o.Id} error:${ex.Message}");
            MoveOrderToErrorQueue(o);
        }
    }
}

The Application can still work with the Invalid Order, moving it to the error queue, accessing its Id, reporting on errors etc. But the Deliver Operation contains the Domain logic of how you want to handle Delivery to blank addresses.

If the Domain logic later changes with further requirements:

public class Order
{
    public string Id {get;set;}
    public string Address {get;set;}

    public void Deliver()
    {
        //check address is valid for delivery
        if(String.IsNullOrWhiteSpace(this.Address))
        {
            throw new Exception("Address not supplied");
        }
        if(Address.Contains("UK"))
        {
            throw new Exception("UK orders not allowed!");
        }
        //delivery code

    }
}

Then you can still process orders on the queue which were generated when UK addresses were allowed and get the expected result.

Its interesting to compare my answer with the one from @VoiceOfUnReason.

I've used a string Address for simplicity and also because there is no absolute definition of what an address is. But if we have a more absolute definition, say his Deposit always has a currency. It's nonsensical to even talk about a deposit with no currency.

In that case yes, you can define a new value type which simply cant exist unless you have specified the currency and loads of potential errors in your code will simply not be possible.

But you have to be sure its a fundamental thing. Otherwise you are asking for trouble later!

  • Thanks. +1 for the historical perspective I.e. An object valid yesterday may not be valid today because of changing validation rules. Which option would you use? Is it in the list from my post? – w0051977 Oct 14 '17 at 13:31
  • erm, not really, I mean the validation of VM for the form post is obvious, but not really part of the Domain. Have the Operation fail, so Order.Purchase() would throw an exception and be handled by whatever app was doing the processing – Ewan Oct 14 '17 at 14:00
  • Thanks for clarifying that. That is option five. Is that right? – w0051977 Oct 14 '17 at 14:04
  • No, I don't think any of the options you list are valid solutions – Ewan Oct 14 '17 at 14:40
  • 1
    @Timo unfortunately that doesn't really work in practice.The problem is that the definition of valid changes while the system is live and in bizarre ways. You would require BeforeHotFixv6Order and ThatTimeWeGaveAwayFreePensOrder – Ewan Feb 22 at 14:15
7

A good search term to review would be "Primitive Obsession"

I am trying to decide where to put the validation in a DDD app.

The usual answer is to put the validation into the constructors/factories for your value objects.

This is the way I've come to think of it: one of the purposes of value objects is to isolate the behavior of the domain from the underlying data representations.

A naive example; let's consider a bank account

Account {
    int currentBalance;

    void deposit (int amount) {
        int workingBalanace = currentBalance;
        workingBalance += amount;
        currentBalance = workingBalance;
    }
}

The design of this entity is tightly coupled to the underlying expression of balance as an int. But that coupling is an accident of implementation, not an underlying domain concern. Int is not taken from the ubiquitous language of the banking domain.

In other words, we should be able to change our decision about how we represent the data in memory without needing to change how our business logic is implemented.

The same naive model, with this extra layer of indirection, might look like

Account {
    Balance currentBalance;

    void deposit (Deposit amount) {
        Balance workingBalance = currentBalance;
        workingBalance = workingBalance.add(amount);
        currentBalance = workingBalance;
    }
}

So Account doesn't care at all about the underlying representations; it just needs to be passed types that support the correct post conditions.

The validation needed to ensure that Deposit is compliant gets pushed closer to the boundary, which is what you want.

So I wouldn't say that the validation happens at every layer - it happens at the boundaries; one boundary being where we convert messages from the outside world into domain concepts, another where we convert messages from the persistent store into domain concepts.

But once you are inside the boundary, you don't need to repeat the validation unless you choose not to capture the fact that the validation was done.

Are you saying that you should not pass primitive types to domain classes?

Not quite. What I'm saying is that the value objects in your domain model provide a layer of indirection between your entities and the underlying data representations.

In the example above, we pass a Deposit to the Account. Where did Deposit come from? It's a value type in our domain, presumably instantiated by taking the message (bytes) that we were passed and transforming them into a more specific domain concept.

Deposit from(int amount) {
    // ...
}

It's likely that the reverse transformation will also exist somewhere, so that we can use a commodity appliance (like a relational database) to store the data

int from(Deposit deposit) {
    // ...
}

I am thinking anti corruption layer. Is that what you are talking about?

Very similar idea; the "legacy system" in this case not so much being a database but instead messaging.

But to my thinking, the key idea is the decoupling of the concept (the interface of the value object manipulated by the domain model) from the underlying representation (data).

  • Are you saying that you should not pass primitive types to domain classes? What about domain properties? My domain objects have properties that are primitive types? – w0051977 Oct 14 '17 at 13:37
  • Can you give an example of validation at the boundary (between service layer and domain layer and from NHibernate to domain layer). – w0051977 Oct 14 '17 at 13:39
  • I am thinking anti corruption layer. Is that what you are talking about? +1 for triggering that thought. – w0051977 Oct 14 '17 at 13:40
  • @w0051977 You can use primitive data types, as long as you do not affect the allowed width of the datatype (such as using a String and only allowing upper-case variants of a word - in that case you should have an UppercaseString value object). A former colleague and a friend of mine published this article also explaining, what you gain from introducing more complex types (VOs) into your system. Check it out, if you want to. – Andy Oct 16 '17 at 5:32
  • I have asked a similar question here: softwareengineering.stackexchange.com/questions/372118/… in case you would like to answer. – w0051977 Jun 5 '18 at 13:55
5

You question seems to be conflating several different notions of validation, so let's try to tease them apart:

  1. We can have validation as a business & domain function — a notion of validating at the domain level, which speaks to interactions with the customers, clients, and suppliers of the business, and actions the business takes, e.g. in accepting and fulfilling orders, etc... At this level, we're talking about validation that would be done manually if not for automation.

  2. We can have validation within modules of the code, such that the code checks for conditions it knows it is not programmed to handle and/or doesn't expect. At this level we're talking about validations that are due to the internal construction of the automation/software architecture.

I think what everyone is saying is that we should perform domain validation on business domain state transitions rather than on computer domain layer crossings or entity contents.

In other words, let's validate for business purposes; let's validate to accomplish state transitions at the business domain level; validate to accomplish business functions.

Let's not do business validation for the purposes of computer domain software implementation concepts like object construction, or setter access, or layer crossing — you'll find none of these in DDD itself as DDD doesn't tell you how to write code or what code you must have.

And in particular, let's not allow validations of type (2) to introduce restrictions (to the operation of the software for the business) that are not otherwise present at the business domain level.

  • Thanks. Can you post some code if it is different to Ewans. – w0051977 Oct 14 '17 at 15:51
  • 1
    I'm not offering code; I'm offering perspective. @Ewans approach is sensible; Khorikovs also has some points to make. – Erik Eidt Oct 14 '17 at 15:54
  • 1
    I agree, you are conflating different types of validation. I think you would get different answers if you took the "DDD" out of your question!! – Ewan Oct 14 '17 at 16:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.