Given our requirement of supporting arbitrary metadata on our validation results, how should we modify the semantic types we’ve already created?
I’m going to follow the example of the Exception type, which includes the oft-overlooked
Data property. This is a dictionary of named values, allowing an arbitrary set of additional properties to be included with the exception.
We could simply add a read/write dictionary to our
ValidationResult base class, but this runs into some messy problems.
At this point our validation semantic types are immutable classes. The addition of a mutable metadata dictionary would be a somewhat odd choice. It seems desirable to choose a design that retains immutability.
Two of our
ValidationResultimplementations currently have transient semantics. One success is much like any other, and combining two aggregates results in a single combined aggregate. Retaining these characteristics is also desirable.
Dictionary use often ends up with lots of magic strings, leading to brittle code that fails in non-obvious ways. Finding a way that avoids this brittleness while still allowing easy consumption would be good.
What sort of API would we like our consumers to experience?
As a minimum, we want to allow one or more name-value pairs to be easily included. Reusing one of our examples from the start of our series, we could use method chaining as an approach to add metadata:
With() method follows the usual pattern for immutable types, returning a new instance with the requested modification.
We still have magic strings in this API, but we can resolve that by adding some supporting constants and extension methods:
While this isn’t too bad to read, it does end up being quite repetitive. The
.WithProperty(nameof(FullName)) ends up being repeated for every validation check we do for the
FullName property. Similarly,
.WithErrorType("Mandatory") gets repeated for every validation check we do for a mandatory field.
What if we could extract those metadata values out as member variables that we reused wherever needed by passing them in with our validation checks?
What we’ve done is to define reusable metadata values that we can mix in with our validation results as required. This simplifies the common case, while still allowing for custom metadata where that makes sense.
Next time, we’ll look at what’s needed to make this API work.