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
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 theIsValid
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 Handle
s is correct, then only valid handles will ever be used.
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
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
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
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