Use the built-in value objects
Before creating another email, URL, currency, or phone type, check Trellis.Primitives. Its input-facing built-ins share the same TryCreate and Result<T> workflow as custom scalar value objects, while keeping common normalization and validation rules in one place. Supporting query values such as GeoBoundingBox are obtained from GeoBounds or static members such as GeoBoundingBox.World instead of being constructed directly.
dotnet add package Trellis.Primitives
Find the type by problem
| Problem | Types |
|---|---|
| Contact and network input | EmailAddress, PhoneNumber, Url, Hostname, IpAddress, Slug |
| Standardized codes | CountryCode, CurrencyCode, LanguageCode |
| Bounded numbers | Age, MonetaryAmount, Percentage |
| Currency-aware values | Money |
| Geographic values | GeoCoordinate, GeoBoundingBox, GeoBounds |
| Weekly local-time availability | WeeklyPeriod, WeeklySchedule |
The generated .NET API pages linked above are the full member reference. This guide focuses on how the types fit into an application.
Validate several built-ins together
The following Program.cs is runnable in a console project that references Trellis.Primitives:
using Trellis;
using Trellis.Primitives;
var result = ContactCard.TryCreate(
email: "ada@example.com",
phone: "+1 (415) 555-1234",
website: "https://example.com");
if (!result.TryGetValue(out var card, out var error))
{
Console.Error.WriteLine(error);
return;
}
Console.WriteLine($"{card.Email.Value} | {card.Phone.Value} | {card.Website.Host}");
public sealed record ContactCard(EmailAddress Email, PhoneNumber Phone, Url Website)
{
public static Result<ContactCard> TryCreate(string? email, string? phone, string? website) =>
EmailAddress.TryCreate(email, nameof(email))
.Combine(PhoneNumber.TryCreate(phone, nameof(phone)))
.Combine(Url.TryCreate(website, nameof(website)))
.Map((validEmail, validPhone, validWebsite) =>
new ContactCard(validEmail, validPhone, validWebsite));
}
Each factory reports against the matching input field. Combine preserves all independent validation failures, and the record is created only on the success track.
Know what validation promises
Built-ins validate their documented shape; they do not replace application policy:
CurrencyCodeaccepts a three-letter ISO-shaped code. If a payment provider supports only selected currencies, enforce that allow-list at the application boundary.PhoneNumbervalidates normalized E.164 shape.GetCountryCode()may still returnMaybe.Nonefor an unassigned prefix.GeoBoundsproduces a conservative candidate box, not an exact distance result.WeeklySchedulemodels recurring local-clock availability, not holidays, capacity, or job scheduling.
When a built-in's meaning or rules do not match your domain, create a custom scalar value object instead of hiding extra rules around every call site.
Continue with structured values
- Money, amounts, and percentages
- Geographic coordinates and radius bounds
- Weekly schedules and overnight periods
Browse all types in the generated Trellis.Primitives API namespace.