4

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.

Dib
  • 2,001
  • 2
  • 29
  • 45
  • 1
    May I suggest that you read http://stackoverflow.com/questions/28395176/should-i-abstract-the-validation-framework-from-domain-layer/28397201#28397201 – plalx Jul 10 '15 at 04:40
  • @plalx - So If I am getting you correctly, I should just let the ChangeName() method of my entity just throw a validation exception and then my validator can catch that and do with it what it wants? – Dib Jul 10 '15 at 06:09
  • 2
    The idea is that an invalid command should almost never be reaching your domain. In that sense, it's an exceptional situation when it does and it's ok to thrown an exception. It's not supposed to reach your domain, because you will be validating on the UI, where using any of the awesome view model validation frameworks out there makes sense. In the end, you end up with what looks like duplication of the rules and it is to some extent, but the UI would generally collect all errors and present them to the user while the domain should throw early and not be concerned about that ceremony. – plalx Jul 10 '15 at 12:52
  • 1
    There are some cases where the UI cannot possibly perform the validation, but these are generally more about the validity of the command itself rather than the validity of it's data members (e.g. trying to modify a comment after 5min on SO). In those cases, we are expecting the commands to reach the domain and we must ensure that we return a descriptive error code allowing the client to understand what went wrong. – plalx Jul 10 '15 at 13:10
  • @plalx +1 for "return a descriptive error code" – Dib Jul 13 '15 at 06:14

1 Answers1

6

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.

Adrian Thompson Phillips
  • 6,893
  • 6
  • 38
  • 69
  • Excellent advice. Thank you.So the consensus is not to proceed with my line of approach and keep entity validation in the entity and bin the attributes. – Dib Jul 10 '15 at 12:48
  • 2
    I think so. If I'm doing MVC I might use the attributes to validate inputs from my view models, but generally for my domain entities I try to avoid decorating them with attributes that only have meaning in other layers. Of course, coding is always one great big trade-off, so if adding attributes gives you a massive pay-off for little cost, it may be the cost effective or sensible solution. Particularly if the application is small/medium sized. But in your case, always-valid entities and data validation done at the point of entry would probably go a long way to achieving good validation. – Adrian Thompson Phillips Jul 10 '15 at 13:34
  • 1
    Does it make sense to validate the commands in the application service layer? (besides the domain which is of course self validating). I have a REST service which is consumed by an AngularJs client and hopefully in the future some native mobile clients such as android and ios... Note that i'd like to auto test my application layer – danfromisrael Jul 12 '15 at 06:17