It’s a common requirement for validation messages to be tagged with additional metadata. We might want to indicate which data entry field is the one with a problem, or perhaps provide a tag for machine consumption that identifies which specific problem was encountered.

If we were still returning all our validation errors as strings, we could invent a number of ways to return a code as a part of the string. For example, we could embed the code in parenthesis at the end of the string like this:

return "The property 'FullName' is mandatory (MissingProperty)";

Or perhaps we’d use a known delimiter to include both a code and the field to which the message applies:

return "MissingProperty:FullName:The property 'FullName' is mandatory.";

Or, worst of all, the developers writing the client code might just start parsing the message intended for human consumption. For example, they might scan "The property 'FullName' is mandatory (MissingProperty)" and look for the property name between single quotes ('), assuming that no-one will ever come along and reformat the messages by converting from FullName to Full Name (or by switching to “typographic quotes”).

Fundamentally all of these approaches are poor, both because they force message consumers to do extra work, and because they make the entire system brittle. All it takes is for things to start to go wrong is one maintenance developer who doesn’t fully understand how the validation code that produces the message interacts with the consumer code that consumes the message. Worse, failures are likely to be subtle - in the form of validation messages that don’t display against the right field in the UX, that sort of thing.

Fortunately, we’re working with rich semantic types, so we don’t have to resort to such levels of complexity - we can simply extend those types with the additional metadata we need.

But … how should we do that?

One approach would be to simply add new properties to ValidationResult and modify the constructor so they’re mandatory:

public abstract class ValidationResult : IEquatable<ValidationResult>
{
    public string Property { get; }
    public string Code { get; }

    private protected ValidationResult(string property, string code)
    {
        Property = property
            ?? throw new ArgumentNullException(nameof(property));
        Code = code
            ?? throw new ArgumentNullException(nameof(code));
    }
}

This turns out to be less than desirable.

Making these properties mandatory is making the assertion that both Property and Code are appropriate for every single use, throughout your application. While this might be true now (and I’d be willing to argue it with you), can you guarantee that will always be the case?

A better approach is to provide a way to attach arbitrary metadata - we then gain the flexibility to both include additional information where it makes sense, and to omit it in other situations.

How would you model this change?

Prior post in this series:
Extending validation with warnings
Next post in this series:
Modelling Validation Metadata

Comments

blog comments powered by Disqus