RA.Utilities.Core.ValueObjects 10.2.0

Prefix Reserved
dotnet add package RA.Utilities.Core.ValueObjects --version 10.2.0
                    
NuGet\Install-Package RA.Utilities.Core.ValueObjects -Version 10.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RA.Utilities.Core.ValueObjects" Version="10.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RA.Utilities.Core.ValueObjects" Version="10.2.0" />
                    
Directory.Packages.props
<PackageReference Include="RA.Utilities.Core.ValueObjects" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RA.Utilities.Core.ValueObjects --version 10.2.0
                    
#r "nuget: RA.Utilities.Core.ValueObjects, 10.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RA.Utilities.Core.ValueObjects@10.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RA.Utilities.Core.ValueObjects&version=10.2.0
                    
Install as a Cake Addin
#tool nuget:?package=RA.Utilities.Core.ValueObjects&version=10.2.0
                    
Install as a Cake Tool

RA.Utilities.Core.ValueObjects

NuGet version Codecov GitHub license NuGet Downloads Documentation

RA.Utilities.Core.ValueObjects provides strongly-typed, validated value objects like Email, Currency, SSN, Money, and IBAN. It solves the problem of passing raw, unvalidated strings and decimals around your domain, where formatting inconsistencies and invalid data can silently propagate.

By creating an instance of a value object, the input is validated and normalized once, at the boundary of your system. After that, an Email that exists is always a valid, normalized email address.

  • Type Safety: You can't accidentally pass a currency code where an email is expected; the compiler enforces intent.
  • Validation at the Boundary: Invalid input throws a BadRequestException with structured ValidationError details (error codes, property names, attempted values) that RA.Utilities.Api middleware can translate into standardized HTTP 400 responses automatically.
  • Consistent Normalization: Values are trimmed and case-normalized on creation, so new Email(" User@Gmail.com ") and new Email("user@gmail.com") are equal.
  • JSON Support: Built-in System.Text.Json converters serialize value objects as plain strings and deserialize them back with validation.
  • Value Equality: All value objects are records, so they compare by value, not reference.

Table of Contents

  • Getting started
  • How It Works
  • Available Value Objects
    • Email
    • Currency
    • SSN
    • Money
    • IBAN
  • JSON Serialization
  • Extensibility
  • Best Practices
  • Additional documentation
  • Contributing

Getting started

You can install the package via the .NET CLI:

dotnet add package RA.Utilities.Core.ValueObjects

Or through the NuGet Package Manager in Visual Studio.


How It Works

This package is designed to fail fast at the edges of your system.

  1. Boundary Validation: When user input (e.g., from a request DTO) creates a value object, the input is validated and normalized immediately. Invalid input throws a BadRequestException.
  2. Guaranteed Validity: Inside your domain, an Email, Currency, or SSN instance that exists is always valid and normalized. No re-validation, no defensive checks.
  3. Automatic Error Responses: In your API project (e.g., using RA.Utilities.Api), a global error-handling middleware catches the BadRequestException and translates it into a standardized HTTP 400 Bad Request response with structured validation details.

Available Value Objects

Email

A validated, normalized email address. Values are trimmed and lower-cased on creation, must not exceed 254 characters, and must match a standard email format.

Usage:

public void RegisterUser(string emailInput)
{
    var email = new Email(emailInput); // throws BadRequestException if invalid

    // LocalPart == "user", Domain == "gmail.com"
    var existing = _userRepository.FindByEmail(email);
    if (existing is not null)
    {
        throw new ConflictException("User", email);
    }
    // ... registration logic
}

Key members:

  • Value: the normalized address (e.g., user@gmail.com).
  • LocalPart / Domain: the parts before and after the @ sign.
  • Parse / TryParse: IParsable<Email> support for route, query, and header binding in minimal APIs.
  • Implicit conversion to string.

Currency

A validated currency code. Values are trimmed and upper-cased on creation and must be exactly 3 characters, as defined by ISO 4217 (e.g., EUR, USD).

Usage:

public Money GetPrice(string currencyCode)
{
    var currency = new Currency(currencyCode); // throws BadRequestException if not exactly 3 characters
    return new Money(19.99m, currency);
}

Key members:

  • Value: the normalized code (e.g., EUR).
  • Parse / TryParse: IParsable<Currency> support for minimal API binding.
  • Implicit conversion to string.

SSN

A validated social security number. Values are trimmed and upper-cased on creation, must not exceed 10 characters, and must match the format ^[A-Z][0-9]{8}[A-Z]$ (a letter, eight digits, a letter).

Usage:

public void UpdateEmployee(string ssnInput)
{
    var ssn = new SSN(ssnInput); // throws BadRequestException if invalid
    _employeeRepository.Update(ssn, update);
}

Key members:

  • Value: the normalized SSN (e.g., J01234567R).
  • Parse / TryParse: IParsable<SSN> support for minimal API binding.
  • Implicit conversion to string.

Money

A monetary amount paired with a Currency. Arithmetic is guarded so you can't combine amounts in different currencies.

Usage:

var price = Money.PositiveMoney(19.99m, new Currency("EUR")); // throws BadRequestException if negative
var shipping = Money.PositiveMoney(4.99m, new Currency("EUR"));
var discount = Money.PositiveMoney(4.00m, new Currency("EUR"));

var total = price.Add(shipping); // 24.98 EUR
var net = price.Subtract(discount); // 15.99 EUR
// total.Add(new Money(5m, new Currency("USD"))) would throw BadRequestException (CurrencyMismatch)

Key members:

  • Amount (decimal) and Currency.
  • Money.PositiveMoney(amount, currency): factory that rejects negative amounts.
  • Add(other): adds two amounts; throws BadRequestException if the currencies differ.
  • Subtract(other): subtracts two amounts; throws BadRequestException if the currencies differ.

IBAN

A validated, normalized International Bank Account Number. Values are trimmed, stripped of grouping separators (spaces, hyphens, backslashes, apostrophes) and upper-cased on creation, checked against the ISO 13616 structural pattern (13–34 alphanumeric characters), and verified using the ISO 7064 mod-97 check digit algorithm.

Usage:

public void ProcessPayment(string rawIban)
{
    var iban = new IBAN(rawIban); // throws BadRequestException if invalid

    // Value == "GB82WEST12345698765432", CountryCode == "GB"
    _paymentGateway.Transfer(iban, amount);
}

Key members:

  • Value: the normalized, compact IBAN (e.g. GB82WEST12345698765432).
  • CountryCode: the 2-letter ISO 3166-1 alpha-2 country code (e.g. GB).
  • Parse / TryParse: IParsable<IBAN> support for minimal API binding.
  • Implicit conversion to string.

JSON Serialization

Every value object carries a built-in System.Text.Json converter (ValueObjectJsonConverter<T>). Value objects serialize as plain strings and deserialize back through validation:

string json = JsonSerializer.Serialize(new Email("User@Gmail.com"));
// json == "\"user@gmail.com\""

Email? email = JsonSerializer.Deserialize<Email>("\" User@Gmail.com \"");
// email.Value == "user@gmail.com" (normalized)

// Invalid input throws JsonException, as expected from a JsonConverter
JsonSerializer.Deserialize<Currency>("\"EURO\""); // throws JsonException

Extensibility

The shared behavior is defined by IValueObject<TSelf> (a normalized Value string plus IParsable<TSelf>). To create your own string-based value object with the same JSON converter support, implement the interface and reuse the generic converter:

[JsonConverter(typeof(ValueObjectJsonConverter<Sku>))]
public sealed partial record Sku : IValueObject<Sku>
{
    public string Value { get; }

    public Sku(string? value)
    {
        // validate and normalize...
        Value = value.Trim().ToUpperInvariant();
    }

    public static Sku Parse(string s, IFormatProvider? provider) => new(s);

    public static bool TryParse(string? s, IFormatProvider? provider, out Sku result) { /* ... */ }
}

Best Practices

  1. Validate at the Boundary, Trust Inside the Domain: Create value objects as early as possible (e.g., when mapping request DTOs). After that, rely on the type system: an Email that exists is valid.

  2. Prefer Value Objects over Primitive Strings in Signatures: Passing Email instead of string makes invalid states unrepresentable and eliminates duplicated validation logic across your application.

  3. Let the API Layer Handle Failures: Don't catch BadRequestException in your domain or application layers for flow control. Let it bubble up to the RA.Utilities.Api middleware, which produces a standardized, structured HTTP 400 response.

  4. Use Money for Amounts with Currency: Never store a monetary amount as a bare decimal. Money prevents cross-currency arithmetic mistakes at compile time and runtime.


Additional documentation

For more information on how this package fits into the larger RA.Utilities ecosystem, please see the main repository documentation.


Contributing

Contributions are welcome! If you have a suggestion for a new value object or find a bug, please open an issue to discuss it. Please follow the contribution guidelines outlined in the other projects in this repository.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on RA.Utilities.Core.ValueObjects:

Package Downloads
RA.Utilities.OpenApi

This package provides utilities to generate and transform OpenAPI documents for ASP.NET Core web APIs.

RA.Utilities.Data.EntityFramework

Provides generic repository base classes for implementing the Repository and Unit of Work patterns with Entity Framework Core.

RA.Utilities.Application.Validation

RA.Utilities.Application.Validation provides reusable validators and helpers built on FluentValidation for validating incoming requests. It aggregates multiple validators asynchronously and converts FluentValidation failures into structured BadRequestException/ValidationError results.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.2.0 192 10/18/2026
10.1.0 107 10/15/2026
10.0.0 113 9/23/2026