38

I still looking best practice for domain model validation. Is that good to put the validation in constructor of domain model ? my domain model validation example as follows:

public class Order
 {
    private readonly List<OrderLine> _lineItems;

    public virtual Customer Customer { get; private set; }
    public virtual DateTime OrderDate { get; private set; }
    public virtual decimal OrderTotal { get; private set; }

    public Order (Customer customer)
    {
        if (customer == null)
            throw new  ArgumentException("Customer name must be defined");

        Customer = customer;
        OrderDate = DateTime.Now;
        _lineItems = new List<LineItem>();
    }

    public void AddOderLine //....
    public IEnumerable<OrderLine> AddOderLine { get {return _lineItems;} }
}


public class OrderLine
{
    public virtual Order Order { get; set; }
    public virtual Product Product { get; set; }
    public virtual int Quantity { get; set; }
    public virtual decimal UnitPrice { get; set; }

    public OrderLine(Order order, int quantity, Product product)
    {
        if (order == null)
            throw new  ArgumentException("Order name must be defined");
        if (quantity <= 0)
            throw new  ArgumentException("Quantity must be greater than zero");
        if (product == null)
            throw new  ArgumentException("Product name must be defined");

        Order = order;
        Quantity = quantity;
        Product = product;
    }
}

Thanks for all of your suggestion.

47

There's an interesting article by Martin Fowler on that subject that highlights an aspect most people (including me) tend to overlook:

But one thing that I think constantly trips people up is when they think object validity on a context independent way such as an isValid method implies.

I think it's much more useful to think of validation as something that's bound to a context - typically an action that you want to do. Is this order valid to be filled, is this customer valid to check in to the hotel. So rather than have methods like isValid have methods like isValidForCheckIn.

From this follows that the constructor should not do validation, except perhaps some very basic sanity checking shared by all contexts.

Again from the article:

In About Face Alan Cooper advocated that we shouldn't let our ideas of valid states prevent a user from entering (and saving) incomplete information. I was reminded by this a few days ago when reading a draft of a book that Jimmy Nilsson is working on. He stated a principle that you should always be able to save an object, even if it has errors in it. While I'm not convinced that this should be an absolute rule, I do think people tend to prevent saving more than they ought. Thinking about the context for validation may help prevent that.

  • Thank goodness someone said this. Forms that have 90% of the data but won't save anything are unfair to users, who often make up the other 10% just to not lose data, so all the validation has done is force the system to lose track of which 10% was made up. Similar issues can happen on the back end - say a data import. I've found it's usually better to try to work properly with invalid data than to try to prevent it from ever happening. – psr Nov 15 '11 at 18:23
  • 2
    @psr Do you even need back-end logic if your data is not persisted? You can leave all the manipulation on the client side if your data has no meaning on your business model. Would be also waste of resources to send messages back and forth (client - server) if the data is meaningless. So we get back to the ideea of "never allowing you domain objects to enter in invalid state!" . – Geo C. Jun 9 '14 at 0:20
  • 2
    I wonder why so many votes for such an ambiguous answer. When using DDD , sometimes there are some rules that simply check if some data is INT or is in a range. For example when you let your app user to choose some constraints on it's products (how many times can someone preview my product, and in what days interval of a month). Here both constraints should be int and one of them should be in a range of 0-31. This seems data format validation that in a non DDD environment would fit in a service or controller. But in DDD I'm on the side of keeping validaion in the domain (90% of it). – Geo C. Jun 9 '14 at 0:27
  • 2
    Enforcing the upper layers to know too much about the domain for keeping it in a valid state smells like bad bad design. The domain should be the one that guarantees it's state to be valid. Moving too much on the shoulders of the upper layers can make your domain anemic and you could slip some imporatant constraints that could hurt your business. What i realise now, a proper generalization would be to keep your validation as close to your persistence as possible, or as close to your data manipulation code (when it's manipulated to reach a final state). – Geo C. Jun 9 '14 at 0:31
  • P.S. I don't mix authorization (is allowed to do something), authentication (did the message came from the right location or was sent by the right client, both being identified by api key/token/username or anything else) with format validation or business rules. When i say 90% i mean those business rules that most of them also include format validation. Ofcourse format validation can be in upper layers, but most of them will be in the domain (even email address format that will be validated in the EmailAddress value object). – Geo C. Jun 9 '14 at 1:03
6

Despite the fact this question is a little stale, I'd like to add something worthwhile:

I'd like to agree with @MichaelBorgwardt and extend by bringing up testability. In "Working Effectively with Legacy Code", Michael Feathers talks a lot about obstacles to testing and one of those obstacles is "difficult to construct" objects. Constructing an invalid object should be possible, and as Fowler suggests, context dependent validity checks should be able to identify those conditions. If you can't figure out how to construct an object in a test harness you're going to have trouble testing your class.

Regarding validity I like to think of control systems. Control systems work by constantly analyzing the state of an output and applying corrective action as the output deviates from the set point, this is called closed loop control. Closed loop control intrinsically expects deviations and acts to correct them and that's how the real world works, which is why all real control systems typically use closed loop controllers.

I think using context dependent validation and easy to construct objects will make your system easier to work with down the road.

  • 1
    Many times objects only appear difficult to construct. For instance in this case you could bypass the public constructor by creating a Wrapper class that inherits from the class being tested and allows you to create an instance of the base object in an invalid state. This is where using the correct access modifiers on classes and constructors comes into play and can really be detrimental to testing if used improperly. Additionally avoiding "sealed" classes and method except where appropriate will go a long way to making a code easier to test. – P. Roe Mar 4 '15 at 21:49
4

As I'm sure you already know...

In object-oriented programming, a constructor (sometimes shortened to ctor) in a class is a special type of subroutine called at the creation of an object. It prepares the new object for use, often accepting parameters which the constructor uses to set any member variables required when the object is first created. It is called a constructor because it constructs the values of data members of the class.

Checking validity of the data passed in as c'tor parameters is definitely valid in the constructor - otherwise you're possibly allowing the construction of an invalid object.

However (and this is just my opinion, can't find any good docs on it at this point) - if data validation requires complex operations (such as async operations - perhaps server-based validation if developing a desktop app), then it's better put in an initialization or explicit validation function of some sort and the members set to default values (such as null) in the c'tor.


Also, just as a side note as you included it in your code sample...

Unless you're doing further validation (or other functionality) in AddOrderLine, I'd most likely expose the List<LineItem> as a property rather than have Order act as a facade.

  • Why expose the container? What does it matter to upper layers what the container is? It is perfectly reasonable to have an AddLineItem method. In fact, for DDD, this is preferred. If List<LineItem> is changed to a custom collection object, then the exposed property and everything that depended on a List<LineItem> property are subject to change, error and exception. – IAbstract Sep 27 '16 at 9:33
4

Validation should be performed as soon as possible.

Validation in any context, weither Domain model or any other way of writing software, should serve the purpose of WHAT you want to validate and at which level you are at the moment.

Based on your question, I guess the answer would be to split the validation.

  1. Property validation checks if the value for that property is correct e.g. when a range between 1-10 is expeced.

  2. Object validation guarantees that all properties on the object are valid in conjunction with each other. e.g. BeginDate is before EndDate. Suppose you read a value from the data store and both BeginDate and EndDate are initialized to DateTime.Min by default. When setting the BeginDate, there is no reason to enforce the "must be before EndDate" rule, since this does not apply YET. This rule should be checked AFTER all properties have been set. This can be called at the aggregate root level

  3. Validation should also be preformed on the aggregate (or aggregate root) entity. An Order object may contain valid data and so do it's OrderLines. But then a business rule states that no order may be over $1,000. How would you enforce this rule in in some cases this IS allowed. you can't just add a "do not validate amount" property since this would lead to abuse (sooner or later, maybe even you, just to get this "nasty request" out of the way).

  4. next there is validation at the presentation layer. Are you realy going to send the object over the network, knowing it will fail? Or will you spare the user this burdon and inform him as soon as he enters an invalid value. e.g. most of the times your DEV environment will be slower than production. Would you like to wait for 30sec before you're informed of "you forgot this field AGAIN during yet ANOTHER test run", especially when there is a production bug to be fixed with your boss breathing down your neck?

  5. Validation at the persistence level is supposed to be as close to property value validation as possible. This will help prevent exceptions with reading "null" or "invalid value" errors when using mappers of any kind or plain old data readers. Using stored procedures does solve this problem, but requires to write the same valiation logic AGAIN and execute it AGAIN. And stored procedures are the DB admin domain, so don't try to do HIS job as well (or worse bother him with this "nitty picking he's not getting payed for".

so to tell it with some famous words "it depends", but atleast now you know WHY it depends.

I wish I could place all this in a single place, but unfortunately, this can't be done. Doing this would place a dependency on a "God object" containing ALL validation for ALL layers. You don't want to go down that dark path.

For this reason I only throw validation exceptions a property level. All other levels I use ValidationResult with an IsValid method to gather all "broken rules" and pass them to the user in a single AggregateException.

When propagating up the call stack, I then gather these again in AggregateExceptions until I reach the presentation layer. The service layer can throw this exception straight to the client in case of WCF as a FaultException.

This allows me to take the exception and either split it up to show individual errors at each input control or flatten it and show it in a single list. The choice is yours.

this is why I also mentioned the presentation validation, to short circuit these as much as possible.

In case you're wondering why I also have the validation at the aggregation level (or service level if you like), it's because I don't have a crystal ball telling me who will be using my services in the future. You'll have enough trouble finding your own mistakes to prevent others from making there mistakes yours :) by inputting invalid data.e.g. you administer application A, but application B feeds some data using your service. Guess who they ask first when there's a bug? The administrator of application B will happily inform the user "there is no error at my end, I just feed in the data".

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