Skip to main content

AdCodicem.ValueObjects

Single-value DDD value objects for .NET, with no reflection and no allocation on the paths that matter.

Declare the type and its rules once; the framework carries them into JSON, the database, model binding and the OpenAPI document, so they cannot drift apart.

[ValueObject<string>(
MinLength = 15,
MaxLength = 34,
Pattern = "^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$",
SchemaFormat = "iban")]
public readonly partial struct Iban
{
private static string NormalizeCore(string value) => /* strip separators, upper-case */;

private static ValidationResult ValidateCore(in string value)
=> HasValidCheckDigits(value)
? ValidationResult.Success
: ValidationResult.InvalidFormat("The IBAN check digits are incorrect.");
}

var iban = Iban.Create("fr76 3000 6000 0112 3456 7890 189");
iban.Value // "FR7630006000011234567890189"
JsonSerializer.Serialize(new { iban }) // {"iban":"FR7630006000011234567890189"}

Normalize, then validate, then assign

A non-default instance is by construction normalized and valid — everywhere except the EF Core read path, which trusts values this same application already validated.

Rules declared once

MaxLength = 34 validates the value, sizes the EF Core column, and becomes the OpenAPI maxLength keyword. Anything added to [ValueObject<T>] feeds all three.

No reflection, no extra allocation

Holding 100 000 struct wrappers allocates exactly what holding 100 000 bare values allocates, to the byte. The generated equality and hashing keep dictionary lookups and sorts allocation-free too.