Trellis.DomainDrivenDesign 3.0.0-alpha.158

This is a prerelease version of Trellis.DomainDrivenDesign.
dotnet add package Trellis.DomainDrivenDesign --version 3.0.0-alpha.158
                    
NuGet\Install-Package Trellis.DomainDrivenDesign -Version 3.0.0-alpha.158
                    
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="Trellis.DomainDrivenDesign" Version="3.0.0-alpha.158" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Trellis.DomainDrivenDesign" Version="3.0.0-alpha.158" />
                    
Directory.Packages.props
<PackageReference Include="Trellis.DomainDrivenDesign" />
                    
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 Trellis.DomainDrivenDesign --version 3.0.0-alpha.158
                    
#r "nuget: Trellis.DomainDrivenDesign, 3.0.0-alpha.158"
                    
#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 Trellis.DomainDrivenDesign@3.0.0-alpha.158
                    
#: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=Trellis.DomainDrivenDesign&version=3.0.0-alpha.158&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Trellis.DomainDrivenDesign&version=3.0.0-alpha.158&prerelease
                    
Install as a Cake Tool

Domain Driven Design

NuGet Package

Building blocks for implementing Domain-Driven Design tactical patterns in C# with functional programming principles.

Installation

dotnet add package Trellis.DomainDrivenDesign

Quick Start

Entity

Objects with unique identity. Equality based on ID.

public partial class CustomerId : RequiredGuid<CustomerId> { }

public class Customer : Entity<CustomerId>
{
    public string Name { get; private set; }
    
    private Customer(CustomerId id, string name) : base(id)
    {
        Name = name;
    }
    
    public static Result<Customer> TryCreate(string name) =>
        name.ToResult()
            .Ensure(n => !string.IsNullOrWhiteSpace(n), Error.Validation("Name required"))
            .Map(n => new Customer(CustomerId.NewUniqueV7(), n));
}

Value Object

Immutable objects with no identity. Equality based on all properties.

public class Money : ValueObject
{
    public decimal Amount { get; }
    public string Currency { get; }
    
    private Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }
    
    public static Result<Money> TryCreate(decimal amount, string currency = "USD") =>
        (amount, currency).ToResult()
            .Ensure(x => x.amount >= 0, Error.Validation("Amount cannot be negative"))
            .Map(x => new Money(x.amount, x.currency));
    
    protected override IEnumerable<IComparable> GetEqualityComponents()
    {
        yield return Amount;
        yield return Currency;
    }
}

Aggregate

Cluster of entities and value objects treated as a unit. Manages domain events.

public record OrderCreated(OrderId Id, CustomerId CustomerId, DateTime OccurredAt) : IDomainEvent;

public class Order : Aggregate<OrderId>
{
    private readonly List<OrderLine> _lines = [];
    
    public CustomerId CustomerId { get; }
    public OrderStatus Status { get; private set; }
    
    private Order(OrderId id, CustomerId customerId) : base(id)
    {
        CustomerId = customerId;
        Status = OrderStatus.Draft;
        DomainEvents.Add(new OrderCreated(id, customerId, DateTime.UtcNow));
    }
    
    public static Result<Order> TryCreate(CustomerId customerId) =>
        new Order(OrderId.NewUniqueV7(), customerId).ToResult();
    
    public Result<Order> AddLine(ProductId productId, string name, Money price, int qty) =>
        this.ToResult()
            .Ensure(_ => Status == OrderStatus.Draft, Error.Validation("Order not editable"))
            .Ensure(_ => qty > 0, Error.Validation("Quantity must be positive"))
            .Tap(_ => _lines.Add(new OrderLine(productId, name, price, qty)));
}

Specification

Encapsulate business rules as composable, storage-agnostic expression trees.

public class HighValueOrderSpec(decimal threshold) : Specification<Order>
{
    public override Expression<Func<Order, bool>> ToExpression() =>
        order => order.TotalAmount > threshold;
}

// Compose specifications
var spec = new OverdueOrderSpec(now).And(new HighValueOrderSpec(500m));

// Use with EF Core
var orders = await dbContext.Orders.Where(spec).ToListAsync();

// In-memory evaluation
if (spec.IsSatisfiedBy(order))
    // order matches

Domain Events

Publish events after persisting:

if (order.IsSuccess)
{
    await repository.SaveAsync(order.Value);
    
    foreach (var evt in order.Value.UncommittedEvents())
        await eventBus.PublishAsync(evt);
    
    order.Value.AcceptChanges();
}

Core Types

Type Purpose Equality
Entity<TId> Objects with identity By ID
ValueObject Immutable, no identity By all properties
ScalarValueObject<TSelf, T> Wraps single primitive By value
Aggregate<TId> Consistency boundary + events By ID
Specification<T> Composable business rules
IDomainEvent Marker for domain events

Best Practices

  1. Use entities when identity mattersCustomer : Entity<CustomerId>
  2. Keep aggregates small — Include only what's needed for invariants
  3. Reference other aggregates by IDpublic CustomerId CustomerId { get; } not public Customer Customer { get; }
  4. Use Maybe<T> for optional propertiesMaybe<Url> Website instead of Url?
  5. Enforce invariants in aggregate root — Use Result<T> for validation
  6. Use domain events for side effects — Not direct service calls
  7. Make value objects immutable — No setters

License

MIT — see LICENSE for details.

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

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
3.0.0-alpha.158 116 4/5/2026
3.0.0-alpha.157 74 4/4/2026
3.0.0-alpha.140 79 3/30/2026
3.0.0-alpha.137 82 3/27/2026
3.0.0-alpha.135 63 3/26/2026
3.0.0-alpha.127 75 3/23/2026
3.0.0-alpha.123 69 3/19/2026
3.0.0-alpha.118 85 3/14/2026
3.0.0-alpha.111 72 3/12/2026
3.0.0-alpha.104 91 3/9/2026
3.0.0-alpha.100 65 3/4/2026
3.0.0-alpha.99 63 3/4/2026
3.0.0-alpha.98 70 3/3/2026
3.0.0-alpha.95 71 3/2/2026
3.0.0-alpha.94 72 3/2/2026
3.0.0-alpha.93 74 3/1/2026
3.0.0-alpha.92 96 2/28/2026
3.0.0-alpha.83 70 2/27/2026