10

Commonly domain objects have properties which can be represented by a built-in type but whose valid values are a subset of the values which may be represented by that type.

In these cases, the value can be stored using the built-in type but it is necessary to ensure values are always validated at the point of entry, otherwise we might end up working with an invalid value.

One way to solve this is to store the value as a custom struct which has a single private readonly backing field of the built-in type and whose constructor validates the provided value. We can then always be sure of only using validated values by using this struct type.

We can also provide cast operators from and to the underlying built-in type so that values can seamlessly enter and exit as the underlying type.

Take as an example a situation where we need to represent the name of a domain object, and valid values are any string which is between 1 and 255 characters in length inclusive. We could represent this using the following struct:

public struct ValidatedName : IEquatable<ValidatedName>
{
    private readonly string _value;

    private ValidatedName(string name)
    {
        _value = name;
    }

    public static bool IsValid(string name)
    {
        return !String.IsNullOrEmpty(name) && name.Length <= 255;
    }

    public bool Equals(ValidatedName other)
    {
        return _value == other._value;
    }

    public override bool Equals(object obj)
    {
        if (obj is ValidatedName)
        {
            return Equals((ValidatedName)obj);
        }
        return false;
    }

    public static implicit operator string(ValidatedName x)
    {
        return x.ToString();
    }

    public static explicit operator ValidatedName(string x)
    {
        if (IsValid(x))
        {
            return new ValidatedName(x);
        }
        throw new InvalidCastException();
    }

    public static bool operator ==(ValidatedName x, ValidatedName y)
    {
        return x.Equals(y);
    }

    public static bool operator !=(ValidatedName x, ValidatedName y)
    {
        return !x.Equals(y);
    }

    public override int GetHashCode()
    {
        return _value.GetHashCode();
    }

    public override string ToString()
    {
        return _value;
    }
}

The example shows the to-string cast as implicit as this can never fail but the from-string cast as explicit as this will throw for invalid values, but of course these could both be either implicit or explicit.

Note also that one can only initialize this struct by way of a cast from string, but one can test whether such a cast will fail in advance using the IsValid static method.

This would seem to be a good pattern to enforce validation of domain values which can be represented by simple types, but I don't see it used often or suggested and I'm interested as to why.

So my question is: what do you see as being the advantages and disadvantages of using this pattern, and why?

If you feel that this is a bad pattern, I would like to understand why and also what you feel is the best alternative.

NB I originally asked this question on Stack Overflow but it was put on hold as primarily opinion-based (ironically subjective in itself) - hopefully it can enjoy more success here.

Above is the original text, below a couple more thoughts, partly in response to the answers received there before it went on hold:

  • One of the major points made by the answers was around the amount of boiler plate code necessary for the above pattern, especially when many such types are required. However in defence of the pattern, this could be largely automated using templates and actually to me it doesn't seem too bad anyway, but that is just my opinion.
  • From a conceptual point of view, does it not seem strange when working with a strongly-typed language such as C# to only apply the strongly-typed principle to composite values, rather than extending it to values which can be represented by an instance of a built-in type?
1
  • you could make a templated version that takes a bool(T) lambda Commented Jan 6, 2015 at 15:08

5 Answers 5

4

This is fairly common in ML-style languages like Standard ML/OCaml/F#/Haskell where it's much easier to create the wrapper types. It provides you with two benefits:

  • It allows a piece of code to enforce that a string has undergone validation, without having to take care of that validation itself.
  • It allows you to localize the validation code in one place. If a ValidatedName ever contains an invalid value, you know the error is in the IsValid method.

If you get the IsValid method right, you have a guarantee that any function that receives a ValidatedName is in fact receiving a validated name.

If you need to do string manipulations you can add a public method that accepts a function that takes a String (the value of the ValidatedName) and returns a String (the new value) and validates the result of applying the function. That eliminates the boilerplate of getting the underlying String value and re-wrapping it.

A related use for wrapping values is to track their provenance. E.g. C-based OS APIs sometimes give handles for resources as integers. You can wrap the OS APIs to instead use a Handle structure and only provide access to the constructor to that part of the code. If the code that produces the Handles is correct, then only valid handles will ever be used.

1

what do you see as being the advantages and disadvantages of using this pattern, and why?

Good:

  • It is self contained. Too many validation bits have tendrils reaching into different places.
  • It helps self-documentation. Seeing a method take a ValidatedString makes it much clearer about the semantics of the call.
  • It helps limit validation to one spot rather than needing to be duplicated across public methods.

Bad:

  • The casting trickery is hidden. It's not idiomatic C#, so can cause confusion when reading the code.
  • It throws. Having strings that don't meet validation isn't an exceptional scenario. Doing IsValid before the cast is a little unweildy.
  • It can't tell you why something is invalid.
  • The default ValidatedString is not valid/validated.

I've seen this sort of thing more often with User and AuthenticatedUser sort of things, where the object actually changes. It can be a fine approach, though it seems out of place in C#.

9
  • 1
    Thanks, I think your fourth "con" is the most compelling argument against it yet - using default or an array of the type could give you invalid values (depending on whether zero/null string is a valid value of course). These are (I think) the only two ways to end up with an invalid value. But then, if we WEREN'T using this pattern, these two things would still give us invalid values, but I suppose at least we would know that they needed to be validated. So this could potentially invalidate the approach where the underlying type's default value is not valid for our type.
    – gmoody1979
    Commented Jan 6, 2015 at 15:25
  • All the cons are implementation issues rather than problems with the concept. Additionally I find the "exceptions should be exceptional" a fuzzy and ill-defined concept. The most pragmatic approach is to provide both an exception-based and non-exception-based method and let the caller choose.
    – Doval
    Commented Jan 6, 2015 at 15:31
  • @Doval I agree except as noted in my other comment. The whole point of the pattern is to know for certain that if we have a ValidatedName, it must be valid. This breaks down if the default value of the underlying type is not also a valid value of the domain type. This is of course domain dependent but is more likely to be the case (I would have thought) for string-based types than for numeric types. The pattern works best where the default of the underlying type is also suitable as the default of the domain type.
    – gmoody1979
    Commented Jan 6, 2015 at 15:39
  • @Doval - I generally agree. The concept itself is fine, but it's effectively trying to shoehorn refinement types into a language that doesn't support them. There's always going to be implementation issues.
    – Telastyn
    Commented Jan 6, 2015 at 15:42
  • Having said that, I suppose you could check for the default value on the "outbound" cast and in any other necessary place within methods of the struct and throw if not initialized, but that starts to get messy.
    – gmoody1979
    Commented Jan 6, 2015 at 15:43
0

Your way is quite heavy and intensive. I typically define domain entities like:

public class Institution
{
    private Institution() { }

    public Institution(int organizationId, string name)
    {
        OrganizationId = organizationId;            
        Name = name;
        ReplicationKey = Guid.NewGuid();

        new InstitutionValidator().ValidateAndThrow(this);
    }

    public int Id { get; private set; }
    public string Name { get; private set; }        
    public virtual ICollection<Department> Departments { get; private set; }

    ... other properties    

    public Department AddDepartment(string name)
    {
        var department = new Department(Id, name);
        if (Departments == null) Departments = new List<Department>();
        Departments.Add(department);            
        return department;
    }

    ... other domain operations
}

In the entity's constructor, validation is triggered using FluentValidation.NET, to make sure you cannot create an entity with invalid state. Note that properties are all readonly - you can only set them through the constructor or dedicated domain operations.

Validation of this entity is a separate class:

public class InstitutionValidator : AbstractValidator<Institution>
{
    public InstitutionValidator()
    {
        RuleFor(institution => institution.Name).NotNull().Length(1, 100).WithLocalizedName(() =>   Prim.Mgp.Infrastructure.Resources.GlobalResources.InstitutionName);       
        RuleFor(institution => institution.OrganizationId).GreaterThan(0);
        RuleFor(institution => institution.ReplicationKey).NotNull().NotEqual(Guid.Empty);
    }  
}

These validators can also be easily reused, and you write less boilerplate code. And another advantage is that it's readable.

3
  • Would the downvoter care to explain why my answer was downvoted?
    – L-Four
    Commented Jan 7, 2015 at 8:31
  • Question was about a struct to constrain value types, and you switched to a class without explaining WHY. (Not a downvoter, just making a suggestion.)
    – DougM
    Commented Jan 7, 2015 at 14:44
  • I explained why I find this a better alternative, and this was one of his questions. Thanks for the reply.
    – L-Four
    Commented Jan 7, 2015 at 15:10
0

I like this approach to value types. The concept is great, but I have some suggestions/complains about the implementation.

Casting: I don't like use of casting in this case. The explicit from-string cast is not a problem, but there is not much difference between (ValidatedName)nameValue and new ValidatedName(nameValue). So it seems kind of unnecessary. The implicit to-string cast is worst problem. I think that getting actual string value should be more explicit, because it might accidentally get assigned to string and compiler won't warn you about possible "loss of precision". This kind of precision loss should be explicit.

ToString: I prefer using ToString overloads just for debugging purposes. And I don't think returning the raw value for it is a good idea. This is same issue as with implicit to-string conversion. Getting the internal value should be explicit operation. I believe you are trying to make the structure behave as a normal string to the outside code, but I think in doing so, you are loosing some of the value you get from implementing this kind of type.

Equals and GetHashCode: Structs are using structural equality by default. So your Equals and GetHashCode are duplicating this default behavior. You can remove them and it will be pretty much same thing.

5
  • Casting: Semantically this feels more to me like transformation of a string to a ValidatedName rather than the creation of a new ValidatedName: we're identifying an existing string as being a ValidatedName. Therefore to me the cast seems more correct semantically. Agreed there's little difference in typing (of the fingers on keyboard variety). I disagree on the to-string cast: ValidatedName is a subset of string, so there can never be a loss of precision...
    – gmoody1979
    Commented Jan 7, 2015 at 13:32
  • ToString: I disagree. To me ToString is a perfectly valid method to use outside of debugging scenarios, assuming it fits the requirement. Also in this situation where a type is a subset of another type, I think it makes sense to make the ability transform from the subset to the super-set as easy as possible, so that if the user desires, they can almost treat it as of the super-set type, i.e. string...
    – gmoody1979
    Commented Jan 7, 2015 at 13:35
  • Equals and GetHashCode: Yes structs use structural equality, but in this instance that is comparing the string reference, not the value of the string. Hence we do need to override Equals. I agree that this wouldn't be necessary if the underlying type was a value type. From my understanding of the default GetHashCode implementation for value types (which is fairly limited), this will give the same value but will be more performant. I should really test whether that's the case but it's a bit of a side issue to the main point of the question. Thank you for your answer by the way :-).
    – gmoody1979
    Commented Jan 7, 2015 at 13:47
  • @gmoody1979 Structures are compared using Equals on every field by default. Shouldn't be a problem with strings. Same with GetHashCode. As for the structure being subset of string. I like to think of the type as safety net. I don't want to work with ValidatedName and then accidentally slip to use string. I would prefer if compiler made me explicitly specify that I now want to work with unchecked data.
    – Euphoric
    Commented Jan 7, 2015 at 14:09
  • Sorry yes, good point on Equals. Although the override should perform better given the default behaviour needs to use reflection to do the comparison. Casting: yes possibly a good argument for making it an explicit cast.
    – gmoody1979
    Commented Jan 7, 2015 at 14:23
0

I just happened to run across this question… turns out, back in 2015, the same year you posted this, I had a very similar idea, and I turned it into an open source library — RT.ValueFilters.

It has all of 2 stars lol, but I’ve been using it pretty much the whole time since, and the only annoyance is losing the syntactic sugar of auto-properties. I believe this library addresses most of the concerns raised in the discussion.

https://github.com/richardtallent/RT.ValueFilter

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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