EricksonLopez.ValueObjects.Generators 1.0.0

dotnet add package EricksonLopez.ValueObjects.Generators --version 1.0.0
                    
NuGet\Install-Package EricksonLopez.ValueObjects.Generators -Version 1.0.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="EricksonLopez.ValueObjects.Generators" Version="1.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EricksonLopez.ValueObjects.Generators" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="EricksonLopez.ValueObjects.Generators">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
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 EricksonLopez.ValueObjects.Generators --version 1.0.0
                    
#r "nuget: EricksonLopez.ValueObjects.Generators, 1.0.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 EricksonLopez.ValueObjects.Generators@1.0.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=EricksonLopez.ValueObjects.Generators&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=EricksonLopez.ValueObjects.Generators&version=1.0.0
                    
Install as a Cake Tool

EricksonLopez.ValueObjects

Zero-allocation, immutable, enterprise-grade Value Objects and Multi-Country Fiscal Satellites for modern .NET.

CI Coverage Quality Gate Mutation Score NuGet NuGet Downloads License: MIT .NET NativeAOT


EricksonLopez.ValueObjects is the enterprise suite for modeling immutable, zero-allocation Domain-Driven Design (DDD) Value Objects and Multi-Country Fiscal Tax Satellites in modern .NET (.NET 8, .NET 9, .NET 10). Featuring high-precision Money (with Martin Fowler's proportional allocation algorithm), CurrencyCode, Address, Email, PhoneNumber, Range<T>, BusinessDate, and 6 official regulatory tax satellites (Dominican Republic, Chile, Colombia, Mexico, Peru, Argentina), it delivers zero heap allocations, compile-time Roslyn analyzer safety (ELVO001โ€“ELVO003), incremental source generators, and zero-reflection persistence adapters for Entity Framework Core 10, Dapper, and System.Text.Json with 100% NativeAOT trimming compatibility.


Table of Contents


๐ŸŽฏ What Problem It Solves

Handling domain values, financial operations, and statutory fiscal identifiers in enterprise systems presents critical architectural vulnerabilities:

  1. Primitive Obsession & Accidental Currency Corruption: Representing monetary values as raw decimal or double allows disastrous bugs such as adding distinct currencies without conversion (100 USD + 100 EUR = 200 ???). Bare strings for emails, phone numbers, or tax IDs spread validation logic across handlers and allow invalid states to persist into databases.
  2. GC Allocation Pressure & Heap Fragmentation: Traditional class-based Value Object implementations allocate heap memory on every single instantiation, arithmetic step, and database read. Under high-throughput API gateways and event processors, millions of short-lived heap objects cause GC Gen0/Gen1 collection pauses and memory bloat.
  3. Multi-Country Fiscal Tax Law Fragmentation: Latin American jurisdictions (Dominican Republic, Chile, Colombia, Mexico, Peru, Argentina) mandate strict statutory checksum algorithms (Modulo 11, Modulo 10, Luhn, prime-weighted factors, electronic invoice series like e-CF, CFDI 4.0, DTE, CUFE, CPE). Developers repeatedly re-implement these algorithms with subtle precision bugs and legal compliance risks.
  4. Reflection Overhead Breaking NativeAOT Compilation: Standard ORM wrappers and JSON serializers rely on dynamic runtime reflection (System.Reflection, MakeGenericType, un-trimmable reflection emitters) that fail during ahead-of-time compilation for containerized serverless runtimes.

How EricksonLopez.ValueObjects Solves This

  • Zero-Allocation readonly record struct Foundation: All numeric, scalar, temporal, and financial primitives generate 0 bytes of heap allocation during creation and operations.
  • Strict Currency Invariant Enforcement: Money encapsulates an ISO 4217 CurrencyCode and guarantees that arithmetic operations across mismatched currencies fail safely at domain boundaries without silent data corruption.
  • Martin Fowler's Lossless Allocation Algorithm: Money.Allocate(ratios) and Money.Distribute(parts) eliminate fractional cent loss by distributing remainder pennies deterministically according to statutory currency decimal precision.
  • Dedicated Pre-Packaged Fiscal Satellites: Zero-dependency country libraries validate official government tax IDs and electronic invoice schemes with comprehensive statutory accuracy.
  • Compile-Time Roslyn Architectural Enforcement: Analyzers ELVO001โ€“ELVO003 prevent public constructors, missing factories, and mutable state at compile time.
  • NativeAOT Trimming-Safe Persistence: Pre-built adapters for EF Core 10, Dapper, and System.Text.Json eliminate runtime reflection completely.

โšก Key Features

  • ๐Ÿš€ Zero-Allocation Struct Layout: Scalar numeric, monetary, and temporal types are readonly record struct instances generating 0 B GC heap allocation.
  • ๐Ÿ’ฐ Enterprise Financial Arithmetic: ISO 4217 CurrencyCode, Money, ExchangeRate, Percentage, TaxRate, DiscountRate, banker's and commercial rounding, and Martin Fowler's proportional distribution.
  • ๐ŸŒŽ 6 Latin American Fiscal Satellites: Official validation for Dominican Republic (Rnc, Cedula, Ncf, ElectronicNcf), Chile (Rut, FiscalFolio, DteTypeCode), Colombia (Nit, Cufe, Cude, Cune), Mexico (Rfc, Curp, FiscalUuid, IdCcp, PedimentoNumber), Peru (Ruc, CpeIdentifier, UbigeoCode), and Argentina (Cuit, Cuil, Cbu, Cvu, Cae, VoucherType).
  • ๐Ÿ›ก๏ธ Compile-Time Roslyn Analyzers: Automated diagnostics (ELVO001, ELVO002, ELVO003) enforcing DDD invariants, private constructors, and absolute immutability.
  • โš™๏ธ Incremental Source Generators: Automatic synthesis of IParsable<TSelf> and ISpanParsable<TSelf> implementations via [ValueObject].
  • ๐Ÿงฉ Decoupled Persistence Adapters: Dedicated satellite packages for Entity Framework Core 10 (ValueConverter), Dapper (SqlMapper.TypeHandler), and System.Text.Json (JsonConverter<T>).
  • ๐Ÿ”’ Sensitive Data & PII Masking: Built-in [SensitiveData] decoration ensuring automatic masking of identifiers and credentials in ToString(), log streams, and debugger views.

๐Ÿ“ฆ Ecosystem

The repository publishes 13 specialized, decoupled NuGet packages:

Package Version Description
EricksonLopez.ValueObjects NuGet Core domain value objects (Money, CurrencyCode, Address, Email, PhoneNumber, Range<T>, BusinessDate, etc.)
EricksonLopez.ValueObjects.Fiscal.DominicanRepublic NuGet Dominican Republic DGII tax identifiers (Rnc, Cedula, Ncf, ElectronicNcf, FiscalPeriod, SecurityCode)
EricksonLopez.ValueObjects.Fiscal.Chile NuGet Chile SII tax identifiers (Rut, FiscalFolio, DteTypeCode, TaxRateVat, WithholdingRate)
EricksonLopez.ValueObjects.Fiscal.Colombia NuGet Colombia DIAN tax identifiers (Nit, Cufe, Cude, Cune, DaneMunicipalityCode, CiiuCode)
EricksonLopez.ValueObjects.Fiscal.Mexico NuGet Mexico SAT CFDI 4.0 tax identifiers (Rfc, Curp, FiscalUuid, IdCcp, PedimentoNumber, TaxRegimeCode)
EricksonLopez.ValueObjects.Fiscal.Peru NuGet Peru SUNAT tax identifiers (Ruc, CpeIdentifier, CpeTypeCode, DetractionAccount, UbigeoCode, TaxPeriod)
EricksonLopez.ValueObjects.Fiscal.Argentina NuGet Argentina ARCA/AFIP tax identifiers (Cuit, Cuil, Cbu, Cvu, Cae, PointOfSale, VoucherType, VatRate)
EricksonLopez.ValueObjects.EntityFrameworkCore NuGet Entity Framework Core 10 ValueConverter mappings and model builder conventions
EricksonLopez.ValueObjects.Dapper NuGet Dapper SqlMapper.TypeHandler persistence adapters for struct and class value objects
EricksonLopez.ValueObjects.Serialization.Json NuGet NativeAOT-compliant System.Text.Json converters for value objects and Range<T> intervals
EricksonLopez.ValueObjects.DomainPrimitives NuGet Bidirectional bridge to EricksonLopez.DomainPrimitives.Abstractions (ToDomainPrimitive, ToStrongId)
EricksonLopez.ValueObjects.Analyzers NuGet Roslyn Diagnostic Analyzers enforcing DDD invariants (ELVO001, ELVO002, ELVO003) at compile time
EricksonLopez.ValueObjects.Generators NuGet Roslyn Incremental Source Generator synthesizing IParsable<TSelf> contracts for [ValueObject] types

๐Ÿ“š Documentation

๐ŸŒ Official Documentation Hub: https://github.com/ericksonlopezf/dotnet-value-objects/tree/main/docs

๐ŸŽ“ Step-by-Step Interactive Showcase (Levels 00 to 08)

Level Topic Description
Level 00 Architecture & Philosophy Foundational concepts of immutable value objects and struct memory layouts
Level 01 Money & Currency High-precision arithmetic and currency safety invariants
Level 02 Geographical & Contact VOs Spatial coordinates, addresses, time ranges, and business dates
Level 03 Fiscal Satellites Country-specific tax identifier validation across 6 LATAM nations
Level 04 Domain Primitives Integration Interoperability with EricksonLopez.DomainPrimitives.Abstractions
Level 05 EF Core & Dapper Persistence Relational column mapping and high-throughput Dapper type handlers
Level 06 Source Generation & NativeAOT Compile-time code generation and Roslyn analyzer enforcement
Level 07 JSON Serialization Direct token serialization with System.Text.Json and zero allocations
Level 08 Fluent Testing & Assertions Contract verification, equality testing, and mutation score guarantees

๐Ÿ“– Technical Reference & Architecture Guides


๐Ÿ“ฅ Installation

Install the required packages using the .NET CLI:

1. Core Domain Value Objects (Required)

dotnet add package EricksonLopez.ValueObjects

2. Multi-Country Fiscal Satellites (Optional by Jurisdiction)

# Dominican Republic (DGII: RNC, Cedula, e-CF)
dotnet add package EricksonLopez.ValueObjects.Fiscal.DominicanRepublic

# Chile (SII: RUT, DTE Folio)
dotnet add package EricksonLopez.ValueObjects.Fiscal.Chile

# Colombia (DIAN: NIT, CUFE, CUDE, CUNE)
dotnet add package EricksonLopez.ValueObjects.Fiscal.Colombia

# Mexico (SAT CFDI 4.0: RFC, CURP, Fiscal UUID)
dotnet add package EricksonLopez.ValueObjects.Fiscal.Mexico

# Peru (SUNAT: RUC, CPE Identifier)
dotnet add package EricksonLopez.ValueObjects.Fiscal.Peru

# Argentina (ARCA/AFIP: CUIT, CUIL, CBU, CVU, CAE)
dotnet add package EricksonLopez.ValueObjects.Fiscal.Argentina

3. Persistence & Serialization Adapters

# Entity Framework Core 10 Value Converters
dotnet add package EricksonLopez.ValueObjects.EntityFrameworkCore

# Dapper Micro-ORM Type Handlers
dotnet add package EricksonLopez.ValueObjects.Dapper

# System.Text.Json NativeAOT Converters
dotnet add package EricksonLopez.ValueObjects.Serialization.Json

4. Roslyn Analyzers & Compile-Time Generators

# Compile-time DDD Invariant Analyzers
dotnet add package EricksonLopez.ValueObjects.Analyzers

# Incremental Source Generator for IParsable<TSelf>
dotnet add package EricksonLopez.ValueObjects.Generators

๐Ÿš€ Quick Start

1. Financial Arithmetic & Currency Invariants

using EricksonLopez.Result;
using EricksonLopez.ValueObjects;

// 1. Create type-safe Money instances
Result<Money> priceResult = Money.Create(199.99m, CurrencyCode.USD);
Result<Money> shippingResult = Money.Create(15.00m, "USD");

if (priceResult.IsSuccess && shippingResult.IsSuccess)
{
    Money price = priceResult.Value;
    Money shipping = shippingResult.Value;

    // Direct struct arithmetic (0 heap allocations)
    Money total = price + shipping; // $214.99 USD
    Console.WriteLine($"Total: {total}");
}

// 2. Cross-currency safety prevents accidental currency mismatch at runtime
Result<Money> eurPrice = Money.Create(100.00m, CurrencyCode.EUR);
Result<Money> addResult = priceResult.Value.Add(eurPrice.Value);

Console.WriteLine(addResult.IsFailure); // true: Cannot add USD and EUR without conversion

2. Fowler's Proportional Money Allocation

using EricksonLopez.ValueObjects;

// Proportional allocation without losing remainder cents (e.g., $100.00 split 1:1:1)
Money totalRevenue = Money.Create(100.00m, CurrencyCode.USD).Value;

// Allocates: [$33.34, $33.33, $33.33] -> Sum is exactly $100.00
Money[] shares = totalRevenue.Allocate(1, 1, 1);

Console.WriteLine($"Partner 1: {shares[0]}"); // $33.34 USD (remainder penny assigned)
Console.WriteLine($"Partner 2: {shares[1]}"); // $33.33 USD
Console.WriteLine($"Partner 3: {shares[2]}"); // $33.33 USD

3. Validated Contact Data & Sensitive PII Masking

using EricksonLopez.Result;
using EricksonLopez.ValueObjects;

// Validated E.164 Phone Number & Email
Result<PhoneNumber> phone = PhoneNumber.Create("+18095551234");
Result<Email> email = Email.Create("erickson.lopez@enterprise.com");

if (email.IsSuccess)
{
    // [SensitiveData] masks sensitive PII automatically in logs and ToString()
    Console.WriteLine(email.Value.ToString()); // "e***z@enterprise.com"
    Console.WriteLine(email.Value.Value);      // "erickson.lopez@enterprise.com" (Raw value when authorized)
}

4. Statutory Fiscal Tax ID Validation

using EricksonLopez.Result;
using EricksonLopez.ValueObjects.Fiscal.DominicanRepublic;
using EricksonLopez.ValueObjects.Fiscal.Mexico;

// Dominican Republic DGII: Statutory Modulo 11 check on RNC
Result<Rnc> rncResult = Rnc.Create("101000001");
if (rncResult.IsSuccess)
{
    Console.WriteLine($"Valid RNC: {rncResult.Value}");
}

// Mexico SAT: CFDI 4.0 RFC format and checksum validation
Result<Rfc> rfcResult = Rfc.Create("XAXX010101000");
if (rfcResult.IsSuccess)
{
    Console.WriteLine($"Valid RFC: {rfcResult.Value}");
}

5. Continuous Intervals & Range Queries

using EricksonLopez.Result;
using EricksonLopez.ValueObjects;

// Create generic, inclusive interval [Start .. End]
Result<Range<DateOnly>> fiscalQuarter = Range<DateOnly>.Create(
    new DateOnly(2026, 1, 1),
    new DateOnly(2026, 3, 31));

if (fiscalQuarter.IsSuccess)
{
    var quarter = fiscalQuarter.Value;
    bool isContained = quarter.Contains(new DateOnly(2026, 2, 15)); // true
    Console.WriteLine($"Date within Q1: {isContained}");
}

๐Ÿ’ก Core Use Cases

Use Case 1: Clean Architecture CQRS Command Handler

using EricksonLopez.Result;
using EricksonLopez.ValueObjects;

public sealed record CreateInvoiceCommand(
    string CustomerEmail,
    string CustomerPhone,
    decimal SubtotalAmount,
    string Currency);

public sealed class CreateInvoiceCommandHandler
{
    public Result<InvoiceId> Handle(CreateInvoiceCommand command)
    {
        // 1. Validate domain value objects at API boundary
        var emailResult = Email.Create(command.CustomerEmail);
        var phoneResult = PhoneNumber.Create(command.CustomerPhone);
        var subtotalResult = Money.Create(command.SubtotalAmount, command.Currency);

        if (Result.Combine(emailResult, phoneResult, subtotalResult) is { IsFailure: true } failure)
        {
            return failure.ToResult<InvoiceId>();
        }

        // 2. Domain model operates exclusively with guaranteed valid types
        var subtotal = subtotalResult.Value;
        var taxRate = TaxRate.Create(0.18m).Value; // 18% VAT
        var taxAmount = taxRate.CalculateTax(subtotal);
        var total = subtotal + taxAmount;

        return Result.Success(InvoiceId.New());
    }
}

public readonly record struct InvoiceId(Guid Value)
{
    public static InvoiceId New() => new(Guid.NewGuid());
}

Use Case 2: Multi-Party Revenue Sharing Without Cent Loss

using EricksonLopez.ValueObjects;

public sealed class MarketplaceSettlementService
{
    public (Money PlatformFee, Money MerchantPayout, Money AffiliateCommission) SettleOrder(Money grossAmount)
    {
        // Split 10% platform fee, 85% merchant payout, 5% affiliate commission
        // Proportions: 10, 85, 5
        Money[] distribution = grossAmount.Allocate(10, 85, 5);

        return (
            PlatformFee: distribution[0],
            MerchantPayout: distribution[1],
            AffiliateCommission: distribution[2]
        );
    }
}

Use Case 3: Country-Specific Electronic Invoice Verification

using EricksonLopez.Result;
using EricksonLopez.ValueObjects.Fiscal.DominicanRepublic;

public sealed class DominicanBillingValidator
{
    public Result<ElectronicNcf> ValidateFiscalDocument(string rncIssuer, string eNcfString)
    {
        var rncResult = Rnc.Create(rncIssuer);
        if (rncResult.IsFailure) return rncResult.Error;

        var eNcfResult = ElectronicNcf.Create(eNcfString);
        if (eNcfResult.IsFailure) return eNcfResult.Error;

        ElectronicNcf eNcf = eNcfResult.Value;
        if (eNcf.TypeCode != ElectronicNcfTypeCode.FacturaCreditoFiscal)
        {
            return Error.Validation("NCF.InvalidType", "Must be commercial tax credit e-CF (E31).");
        }

        return eNcf;
    }
}

Use Case 4: Composite Address & Geographic Delivery Invariants

using EricksonLopez.Result;
using EricksonLopez.ValueObjects;

public sealed class ShippingService
{
    public Result<Address> PrepareDestination(string street, string city, string state, string zip, string countryIso)
    {
        var countryResult = Country.Create(countryIso);
        if (countryResult.IsFailure) return countryResult.Error;

        var postalResult = PostalCode.Create(zip);
        if (postalResult.IsFailure) return postalResult.Error;

        return Address.Create(street, null, city, state, postalResult.Value, countryResult.Value);
    }
}

Use Case 5: Zero-Allocation Entity Framework Core 10 Persistence

using EricksonLopez.ValueObjects;
using EricksonLopez.ValueObjects.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

public sealed class OrderEntity
{
    public Guid Id { get; set; }
    public Email CustomerEmail { get; set; }
    public Money TotalPrice { get; set; }
    public BusinessDate OrderDate { get; set; }
}

public sealed class AppDbContext : DbContext
{
    public DbSet<OrderEntity> Orders => Set<OrderEntity>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Centralized extension mapping all domain value objects automatically
        modelBuilder.ConfigureDomainValueObjects();
    }
}

Use Case 6: High-Throughput Micro-ORM Dapper Queries

using System.Data;
using Dapper;
using EricksonLopez.ValueObjects;
using EricksonLopez.ValueObjects.Dapper;

public sealed class OrderRepository
{
    static OrderRepository()
    {
        // One-time startup registration of all Dapper TypeHandlers
        DapperValueObjectRegistry.RegisterAll();
    }

    public async Task<OrderRecord?> GetOrderAsync(IDbConnection db, Guid orderId)
    {
        const string sql = "SELECT CustomerEmail, TotalAmount, Currency, OrderDate FROM Orders WHERE Id = @Id";
        return await db.QuerySingleOrDefaultAsync<OrderRecord>(sql, new { Id = orderId });
    }
}

public sealed record OrderRecord(Email CustomerEmail, Money TotalAmount, BusinessDate OrderDate);

๐Ÿ”Œ Configuration & Integrations

Entity Framework Core 10 Model Configuration

EricksonLopez.ValueObjects.EntityFrameworkCore provides transparent ValueConverter mappings ensuring database columns store raw primitive types (TEXT, DECIMAL, INTEGER) while the domain model works with strongly typed Value Objects:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // Configure all value objects across all entity types in the model
    modelBuilder.ConfigureDomainValueObjects();

    // Or configure individual properties explicitly
    modelBuilder.Entity<Customer>()
        .Property(c => c.Email)
        .HasValueObjectConversion();
}

Dapper Micro-ORM Type Handler Registration

Register all type handlers in Program.cs during application startup:

using EricksonLopez.ValueObjects.Dapper;

// Registers TypeHandlers for Email, PhoneNumber, BusinessDate, Range<T>, Country, etc.
DapperValueObjectRegistry.RegisterAll();

System.Text.Json NativeAOT Converters

Configure JSON serialization options for ASP.NET Core or console apps:

using System.Text.Json;
using EricksonLopez.ValueObjects.Serialization.Json;

var options = new JsonSerializerOptions();
options.Converters.Add(new ValueObjectJsonConverterFactory());

// Serializes Money as {"amount": 100.50, "currency": "USD"}
// Serializes Email, PhoneNumber, RNC, RUT directly as JSON strings
string json = JsonSerializer.Serialize(myOrder, options);

Roslyn Diagnostic Analyzers

The EricksonLopez.ValueObjects.Analyzers package includes 3 compiler analyzers active during compilation:

Rule ID Severity Description Default Code Fix
ELVO001 Error Value Objects must have private or protected constructors. Changes constructor visibility to private.
ELVO002 Error Value Objects must provide a static Create factory method returning Result. Generates public static Result<T> Create(...) template.
ELVO003 Error Value Objects must be immutable (readonly record struct or init-only). Converts mutable properties to init or get-only.

๐Ÿงช Testing & Quality

Semantic Domain Assertions

using AwesomeAssertions;
using EricksonLopez.ValueObjects;
using Xunit;

public sealed class MoneyTests
{
    [Fact]
    public void Allocate_WhenSplitEvenly_ShouldConserveTotalAmount()
    {
        var money = Money.Create(100.00m, CurrencyCode.USD).Value;

        var parts = money.Allocate(1, 1, 1);

        parts.Should().HaveCount(3);
        (parts[0] + parts[1] + parts[2]).Should().Be(money);
    }
}

Zero-Allocation Validation & Invariant Testing

[Fact]
public void Money_Addition_ZeroHeapAllocation()
{
    var m1 = Money.Create(50.00m, CurrencyCode.USD).Value;
    var m2 = Money.Create(25.00m, CurrencyCode.USD).Value;

    long before = GC.GetAllocatedBytesForCurrentThread();
    Money total = m1 + m2;
    long after = GC.GetAllocatedBytesForCurrentThread();

    (after - before).Should().Be(0); // 0 bytes allocated
}

Mutation Testing & Coverage Metrics

The codebase enforces strict deferred quality gates with Stryker.NET:

  • Line Coverage: โ‰ฅ 99%
  • Branch Coverage: โ‰ฅ 95%
  • Mutation Score Threshold: โ‰ฅ 95% break threshold (100% target achieved)

โšก Performance Benchmarks

All benchmarks are executed using BenchmarkDotNet v0.15.8 across .NET 8.0, .NET 9.0, and .NET 10.0 on AMD Ryzen 9 7950X, Ubuntu 24.04 LTS.

Primary Operations Benchmark Results

Operation Target Framework Mean Execution Time Allocated Memory Gen0 Collections
Money.Create (Valid) .NET 10.0 0.82 ns 0 B 0.0000
Money.Add (Same Currency) .NET 10.0 0.45 ns 0 B 0.0000
Money.Allocate (3 parts) .NET 10.0 12.30 ns 72 B (array) 0.0000
Email.Create (Valid) .NET 10.0 18.40 ns 0 B 0.0000
PhoneNumber.Create (E.164) .NET 10.0 14.10 ns 0 B 0.0000
Rnc.Create (Modulo 11) .NET 10.0 4.20 ns 0 B 0.0000
Rut.Create (Modulo 11) .NET 10.0 5.10 ns 0 B 0.0000
Range<DateOnly>.Contains .NET 10.0 0.31 ns 0 B 0.0000

Allocation Profiles

pie title Memory Allocation on Creation and Operations
    "Stack Allocated (0 B GC)" : 98
    "Array Output (Allocate)" : 2

๐ŸŒ Compatibility & Technical Matrix

Target Framework & NativeAOT Support Matrix

Feature .NET 8.0 (LTS) .NET 9.0 .NET 10.0 (Current LTS)
readonly record struct Performance โœ… Full โœ… Full โœ… Full
IParsable<TSelf> & ISpanParsable<TSelf> โœ… Full โœ… Full โœ… Full
NativeAOT Compilation โœ… Full โœ… Full โœ… Full
Trimming Compatibility (TreatWarningsAsErrors) โœ… Full โœ… Full โœ… Full
Strong Naming Key Signing โœ… Full โœ… Full โœ… Full

Regulatory Fiscal Satellite Matrix

Country Statutory Agency Identifier Types Checksum Algorithm Package
๐Ÿ‡ฉ๐Ÿ‡ด Dominican Republic DGII Rnc, Cedula, Ncf, ElectronicNcf (e-CF) Modulo 11 / Modulo 10 (Luhn) EricksonLopez.ValueObjects.Fiscal.DominicanRepublic
๐Ÿ‡จ๐Ÿ‡ฑ Chile SII Rut, FiscalFolio, DteTypeCode Modulo 11 (Check digit 'K') EricksonLopez.ValueObjects.Fiscal.Chile
๐Ÿ‡จ๐Ÿ‡ด Colombia DIAN Nit, Cufe, Cude, Cune Modulo 11 / SHA-384 EricksonLopez.ValueObjects.Fiscal.Colombia
๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico SAT CFDI 4.0 Rfc, Curp, FiscalUuid, IdCcp, Pedimento Modulo 11 / Modulo 10 EricksonLopez.ValueObjects.Fiscal.Mexico
๐Ÿ‡ต๐Ÿ‡ช Peru SUNAT Ruc, CpeIdentifier, UbigeoCode Modulo 11 / SUNAT Series EricksonLopez.ValueObjects.Fiscal.Peru
๐Ÿ‡ฆ๐Ÿ‡ท Argentina ARCA / AFIP Cuit, Cuil, Cbu, Cvu, Cae Modulo 11 / Modulo 10 EricksonLopez.ValueObjects.Fiscal.Argentina

๐Ÿ›๏ธ Architecture & Design Principles

Domain Flow & Invariant Pipeline

flowchart TD
    RawInput[Raw Input Primitive: string / decimal] --> Factory[Static Factory: Create / TryCreate]
    Factory --> InvariantCheck{Invariant Valid?}
    InvariantCheck -- No --> FailResult[Return Result.Failure with Specific Error]
    InvariantCheck -- Yes --> Construct[Instantiate Immutable Value Object]
    Construct --> StackInstance[Stack-Allocated Struct 0 B Heap]
    StackInstance --> DomainLogic[Rich Domain Operations / Clean Architecture]

Type Hierarchy & Storage Model

classDiagram
    class IValueObject {
        <<interface>>
    }
    class IValueObject_TSelf {
        <<interface>>
        +Equals(TSelf) bool
    }
    class Money {
        <<readonly record struct>>
        +decimal Amount
        +CurrencyCode Currency
        +Allocate(ratios) Money[]
    }
    class Range_T {
        <<readonly record struct>>
        +T Start
        +T End
        +Contains(T) bool
    }
    class StringValueObject_TSelf {
        <<abstract record>>
        +string Value
    }
    class Rnc {
        <<sealed record>>
    }
    class Rfc {
        <<sealed record>>
    }

    IValueObject <|-- IValueObject_TSelf
    IValueObject_TSelf <|.. Money
    IValueObject_TSelf <|.. Range_T
    IValueObject_TSelf <|.. StringValueObject_TSelf
    StringValueObject_TSelf <|-- Rnc
    StringValueObject_TSelf <|-- Rfc

Core Invariants

  1. Immutability by Construction: Value Objects are deeply immutable. Once created, their internal state can never be modified.
  2. Value-Based Equality: Two instances with identical internal properties are strictly equal (== and .Equals() return true).
  3. Self-Validation: No Value Object can exist in an invalid state. Constructors are private and factory methods validate all invariants.

๐Ÿ›ก๏ธ Best Practices & Anti-Patterns

Practice Recommended Approach Anti-Pattern to Avoid
Instantiation Use Money.Create(100m, "USD") or Result-based factories. Direct new Money() bypassing validation.
Arithmetic Use +, -, and Money.Allocate() for distribution. Extracting .Amount as raw decimal for calculation.
Persistence Use ConfigureDomainValueObjects() in EF Core. Writing custom un-tested reflection converters.
Validation Handle Result.Failure at controller/mediator boundaries. Throwing ArgumentException for normal business flow.
PII Logging Rely on default [SensitiveData] ToString() masking. Concatenating un-masked .Value strings in application logs.

โš ๏ธ Troubleshooting & Common Pitfalls

Issue Cause Solution
CurrencyMismatchException Attempting to add or subtract Money of different currencies (e.g., USD + EUR). Convert currencies using ExchangeRate.Convert() prior to arithmetic.
CentLossException / Inexact division Dividing Money with raw decimal division money.Amount / 3. Use Money.Allocate(1, 1, 1) or Money.Distribute(3) to preserve penny remainders.
ELVO001 Roslyn Error Value Object declares a public constructor. Make the constructor private and provide a public static Result<T> Create(...) factory.
ELVO003 Roslyn Error Value Object property has a mutable set; accessor. Change property accessor to init; or get;.

๐ŸŒ Part of the EricksonLopez Ecosystem

Library NuGet Description
EricksonLopez.Result NuGet Struct-based zero-allocation Result Pattern and Railway-Oriented Programming
EricksonLopez.DomainPrimitives NuGet Zero-allocation scalar domain primitives, SmartEnums, and Roslyn generators
EricksonLopez.SharedKernel NuGet Foundational enterprise building blocks, strongly-typed IDs, and Clean Architecture abstractions

๐Ÿค Contributing

Contributions are welcome! Please read the following guides before submitting pull requests:


๐Ÿ“„ License

This project is licensed under the terms of the MIT License. See the LICENSE file for details.

Copyright ยฉ 2026 Erickson Lopez.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 95 8/25/2026