Validation
The StringlyTyped
package includes a simple ValueObject implementation that can be used to enforce validation rules on domain objects. This helps prevent invalid data from being introduced into your domain and allows you to assume that all ValueObjects are valid within your code.
The Validation
class provides a simple way to represent validation results:
// src/StringlyTyped/Validation.cs
public class Validation
{
public string ErrorMessage { get; }
public static readonly Validation Ok = new("");
private Validation(string reason) => ErrorMessage = reason;
public static Validation Invalid(string reason = "")
{
if (string.IsNullOrEmpty(reason))
{
return new Validation("[none provided]");
}
return new Validation(reason);
}
}
The ValueObject
base class provides a Validate()
method that subclasses can override to implement their own validation rules. This method should return Validation.Ok
if the object is valid or Validation.Invalid
with an error message if the object is invalid.
// src/StringlyTyped/ValueObject.cs
public virtual Validation Validate() => Validation.Ok;
Here’s an example of how to implement validation in a CustomerId
ValueObject:
// README.md
public class CustomerId : ValueObject
{
public override Validation Validate() => Value > 0
? Validation.Ok
: Validation.Invalid("Customer IDs cannot be zero or negative.");
}
This example ensures that the CustomerId
value is always positive.
Example Validation Scenarios
Here are some examples of validation scenarios from the test codebase:
- PlayerNumber: Ensures that the player number is between 1 and 12.
// tests/StringlyTyped.SmallTests/Types.cs
public override Validation Validate() =>
Value is >=1 and <=12 ? Validation.Ok : Validation.Invalid("Player number mst be between 1 and 12.");
- EightiesDate: Ensures that the date is within the 1980s.
// tests/StringlyTyped.SmallTests/Types.cs
public override Validation Validate() => Value.Year is >= 1980 and <= 1989 ? Validation.Ok : Validation.Invalid("Must be a date in the 80's");
- MinimalValidation: Ensures that the value is not equal to 1.
// tests/StringlyTyped.SmallTests/Types.cs
public override Validation Validate() =>
Value != 1 ? Validation.Ok : Validation.Invalid();
- Age: Ensures that the age is 18 or over.
// tests/StringlyTyped.SmallTests/Types.cs
public override Validation Validate() => Value >= 18 ? Validation.Ok : Validation.Invalid("Must be 18 or over");
- Dave: Ensures that the name starts with “dave” or “david”.
// tests/StringlyTyped.SmallTests/Types.cs
public override Validation Validate() => Value.StartsWith("dave ", StringComparison.OrdinalIgnoreCase) ||
Value.StartsWith("david ", StringComparison.OrdinalIgnoreCase)
? Validation.Ok
: Validation.Invalid("must be a dave or david");
Handling Validation Exceptions
The ValueObjectValidationException
is thrown when a ValueObject’s Validate()
method returns Validation.Invalid
. This allows you to handle validation errors gracefully.
// samples/StringlyTyped.Examples/ValidationExample.cs
public static void Run()
{
string[] names = new[] { "Dave Grohl", "David Beckham", "Fred Flintstone" };
var processor = new DaveProcessor();
foreach (string name in names)
{
try
{
processor.Process(Dave.From(name));
}
catch (ValueObjectValidationException e)
{
Console.WriteLine(e.Message);
}
}
}
This example demonstrates how to catch ValueObjectValidationException
and handle the error message.
NOTE: This code snippet was taken from the samples/StringlyTyped.Examples/ValidationExample.cs file.
Top-Level Directory Explanations
samples/ - This directory contains example projects demonstrating the usage of StringlyTyped library.
src/ - This directory contains the source code of the StringlyTyped library.
tests/ - This directory contains unit tests for the StringlyTyped library. It includes benchmark tests and small tests.