DDD value object base classes plus a curated set of common value objects (Email, Slug, Money, Cpf, Cnpj, Document, Cep, PhoneNumberBr). Construction returns
Result<T>so failures compose naturally with the rest of your code.
| Problem | What this package gives you |
|---|---|
Re-implementing Equals / GetHashCode for every primitive wrapper |
ValueObject and SingleValueObject<T> base classes — define GetEqualityComponents and you're done. |
| Throwing exceptions during construction | Each common VO returns Result<T> from MStack.ErrorHandling. Failures stay in the Result pipeline. |
| Re-writing CPF / CNPJ / CEP / Phone validation | Production-tested implementations included. |
dotnet add package MStack.ValueObjectsThis brings MStack.ErrorHandling along.
using MStack.ErrorHandling;
namespace MyApp;
public sealed class Coordinates : ValueObject
{
public double Latitude { get; }
public double Longitude { get; }
private Coordinates(double lat, double lng)
{
Latitude = lat;
Longitude = lng;
}
public static Result<Coordinates> Create(double lat, double lng)
{
if (lat is < -90 or > 90)
{
return Error.Validation(nameof(Latitude), "Must be between -90 and 90.");
}
if (lng is < -180 or > 180)
{
return Error.Validation(nameof(Longitude), "Must be between -180 and 180.");
}
return new Coordinates(lat, lng);
}
protected override IEnumerable<object?> GetEqualityComponents()
{
yield return Latitude;
yield return Longitude;
}
}using MStack.ValueObjects;
var emailResult = Email.Create("user@example.com");
var cpfResult = Cpf.Create("390.533.447-05");
var cnpjResult = Cnpj.Create("11.222.333/0001-81");
var docResult = Document.Create("390.533.447-05"); // CPF or CNPJ
var cepResult = Cep.Create("01310-100");
var phoneResult = PhoneNumberBr.Create("(11) 91234-5678");
var moneyResult = Money.Create(99.90m, "BRL");
var slugResult = Slug.Create("hello-world");
if (emailResult.IsFailure) { /* errors live in result.Errors */ }Failures are categorised as ErrorCategory.Validation, so MStack.ErrorHandling will turn them into a 400 Bad Request ProblemDetails automatically when used in a Minimal API.
| Type | Namespace | Notes |
|---|---|---|
ValueObject |
MStack.ValueObjects |
Abstract base. Override GetEqualityComponents. |
SingleValueObject<T> |
MStack.ValueObjects |
Helper for VOs wrapping one value. Provides Value, ToString(), implicit operator T, and GetEqualityComponents. |
Email |
MStack.ValueObjects |
Trim + lowercase, RFC-shaped regex. |
Slug |
MStack.ValueObjects |
Lowercase letters, digits and single hyphens. |
Money |
MStack.ValueObjects |
Decimal + ISO 4217 currency. Add requires same currency. |
Cpf |
MStack.ValueObjects |
Brazilian CPF, full check-digit validation. |
Cnpj |
MStack.ValueObjects |
Brazilian CNPJ, full check-digit validation. |
Document |
MStack.ValueObjects |
CPF or CNPJ — auto-detected by length. |
Cep |
MStack.ValueObjects |
Brazilian postal code, 8 digits. |
PhoneNumberBr |
MStack.ValueObjects |
Brazilian phone, 10 or 11 digits. Rejects toll-free (0800). Includes ToE164(). |
VOs that inherit from SingleValueObject<T> expose Value (raw digits/string), ToFormatted() where formatting makes sense, implicit conversion to the underlying type, and structural equality. Those inheriting directly from ValueObject expose their own domain-specific properties.