Trellis.DomainDrivenDesign
3.0.0-alpha.99
dotnet add package Trellis.DomainDrivenDesign --version 3.0.0-alpha.99
NuGet\Install-Package Trellis.DomainDrivenDesign -Version 3.0.0-alpha.99
<PackageReference Include="Trellis.DomainDrivenDesign" Version="3.0.0-alpha.99" />
<PackageVersion Include="Trellis.DomainDrivenDesign" Version="3.0.0-alpha.99" />
<PackageReference Include="Trellis.DomainDrivenDesign" />
paket add Trellis.DomainDrivenDesign --version 3.0.0-alpha.99
#r "nuget: Trellis.DomainDrivenDesign, 3.0.0-alpha.99"
#:package Trellis.DomainDrivenDesign@3.0.0-alpha.99
#addin nuget:?package=Trellis.DomainDrivenDesign&version=3.0.0-alpha.99&prerelease
#tool nuget:?package=Trellis.DomainDrivenDesign&version=3.0.0-alpha.99&prerelease
Domain Driven Design
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
- Use entities when identity matters —
Customer : Entity<CustomerId> - Keep aggregates small — Include only what's needed for invariants
- Reference other aggregates by ID —
public CustomerId CustomerId { get; }notpublic Customer Customer { get; } - Use
Maybe<T>for optional properties —Maybe<Url> Websiteinstead ofUrl? - Enforce invariants in aggregate root — Use
Result<T>for validation - Use domain events for side effects — Not direct service calls
- Make value objects immutable — No setters
Related Packages
- Trellis.Results — Core
Result<T>type - Trellis.Primitives — RequiredString, RequiredGuid, EmailAddress, and more
- Trellis.Asp — ASP.NET Core integration
License
MIT — see LICENSE for details.
| Product | Versions 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. |
-
net10.0
- Trellis.Results (>= 3.0.0-alpha.99)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Trellis.DomainDrivenDesign:
| Package | Downloads |
|---|---|
|
Trellis.Primitives
Infrastructure and ready-to-use implementations for primitive value objects in Domain-Driven Design. Includes base classes (RequiredString, RequiredGuid) with source generation, plus EmailAddress with RFC 5322 validation. Eliminates primitive obsession with strongly-typed domain primitives. |
|
|
Trellis.Testing
Testing utilities and assertions for Trellis - FluentAssertions extensions, test builders, and fake implementations for Railway-Oriented Programming |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0-alpha.99 | 0 | 3/4/2026 |
| 3.0.0-alpha.98 | 33 | 3/3/2026 |
| 3.0.0-alpha.95 | 44 | 3/2/2026 |
| 3.0.0-alpha.94 | 44 | 3/2/2026 |
| 3.0.0-alpha.93 | 43 | 3/1/2026 |
| 3.0.0-alpha.92 | 55 | 2/28/2026 |
| 3.0.0-alpha.83 | 40 | 2/27/2026 |