Search code examples
c#validationdomain-driven-designdata-annotationscommand-pattern

Command validation using data annotations and DDD "ChangeProperty" methods


Taking the simple Entity below...

public class MyEntity
{
    [MaxLength(100)]
    [Required]
    public string Name { get; private set; }
}

...is it possible to read the data annotations which are decorating the "Name" property and validate the value specified in the ChangeName method so the ValidationResults can be concatenated to other validation results. I assume using MethodInfo or PropertyInfo objects some how?

I have this but it feels very clumsey.

    public ValidationResult ChangeName(string value)
    {
        var property = GetType().GetProperty("Name");
        var attribute = property.GetCustomAttributes(typeof(MaxLengthAttribute), true)[0] as MaxLengthAttribute;
        if (attribute == null) return null; //yield break;

        if (value.Length > attribute.Length)
        {
            return new ValidationResult(NameValidation.NameTooLong);
        }

        return null;
    }

I want to be able to call ChangeName(value) from a method in my validator like so..

    private IEnumerable<ValidationResult> ValidateMyEntity(MyEntityAddCommand command)
    {
        MyEntity myEntity = new MyEntity();
        yield return myEntity.ChangeName(command.Name);
    }

Most things I have read so far say data annotations are for CRUD not DDD, but why not use them as they are a nice way to describe validation behaviour and it will be consistent with the data annotation validation on my MVC view model. Is there a better or more succinct way I can do this? All advice appreciated.


Solution

  • I no longer chase the ideal that validation can (or should) happen in one place. Validation happening in one place only is a nice idea, it'd remove code duplication, but I've never been able to make it happen without encountering a one-size-fits-all problem (where it works 95% of the time, but for that 5% where it doesn't, it costs you a day of development to solve something that should take 10 minutes). Sometimes validation rules can vary depending on the view or part of the application that you're using. Nowadays I validate as data enters my system, be it a web page form or a web service, etc. I also validate by having always-valid domain entities.

    If using MVC, I put my data annotations in my view models. If using on ORM (like EF or NHibernate) I put my annotations on my data entities and restrict these to being hidden by the repositories (my repositories return domain entities).

    I keep my domain entities free of validation type annotations (any annotations if possible). I also ensure that my domain entities are always valid and throw exceptions when something attempts to set my entity to an invalid state/value. This way it signals to me that something has gone wrong, not just in a validation sense, but it tells me that somehow bad data has made it past my first line of defence and that I need to fix my code.