ddd-implementation

star 1

Domain entity and value object patterns — private setters, factory methods, Reconstitute. Use when creating or modifying domain models.

z3d By z3d schedule Updated 3/7/2026

name: ddd-implementation description: Domain entity and value object patterns — private setters, factory methods, Reconstitute. Use when creating or modifying domain models. user-invocable: false

Domain-Driven Design Implementation

Domain Entities

Core Pattern: Entities with private setters, public constructors, and domain behavior.

public class Product
{
    public int Id { get; private set; }
    public string Name { get; private set; } = string.Empty;
    public Money Price { get; private set; } = null!;
    public int Stock { get; private set; }

    protected Product() { } // EF Core constructor

    public Product(string name, string description, Money price, int stock)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(name);
        ArgumentNullException.ThrowIfNull(price);
        ArgumentOutOfRangeException.ThrowIfNegative(stock);

        Name = name;
        Description = description;
        Price = price;
        Stock = stock;
    }

    public void UpdateDetails(string name, Money price)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(name);
        ArgumentNullException.ThrowIfNull(price);
        Name = name;
        Price = price;
    }
}

Value Objects

Core Pattern: Immutable objects with static factory methods and proper equality.

public class Money
{
    public decimal Amount { get; private set; }
    public string Currency { get; private set; } = string.Empty;

    private Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public static Money Create(decimal amount, string currency)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(amount);
        ArgumentException.ThrowIfNullOrWhiteSpace(currency);
        if (currency.Length > 3)
            throw new ArgumentException("Currency code cannot exceed 3 characters", nameof(currency));
        return new Money(amount, currency);
    }

    // Must override Equals(object) and GetHashCode() — convention tests enforce this
}

Entity-Value Object Integration

Use single entity with embedded value objects (not dual representation):

// CORRECT - Single entity with embedded value objects
public class Customer
{
    public int Id { get; private set; }
    public string Name { get; private set; } = string.Empty;
    public Email Email { get; private set; } = null!; // Embedded value object

    public void UpdateEmail(Email newEmail)
    {
        ArgumentNullException.ThrowIfNull(newEmail);
        Email = newEmail;
    }
}

// AVOID - Dual representation creates unnecessary complexity
// public class CustomerValue { }  // Don't create this
// public class CustomerEntity { } // When you already have Customer

Reconstitute Pattern (Test-Only)

internal static factory method for rebuilding aggregates in arbitrary states. Visible to the test project via InternalsVisibleTo.

internal static Order Reconstitute(int id, int customerId, DateTime orderDate,
    OrderStatus status, DateTime lastUpdated, List<OrderItem> items)

Why it exists: The public Order(customerId) constructor enforces creation-time invariants (status = Pending, empty items). Property-based fuzz tests need to create orders in arbitrary states (e.g., Shipped, Delivered) without going through the full state machine.

Not for production handlers: Command handlers load aggregates via EF Core with .Include(o => o.Items) on a tracked entity, mutate through domain methods, and call SaveChangesAsync. EF Core detects only the changed properties. Do not use AsNoTracking + Reconstitute + Update — that marks all columns modified and creates lost-update risks.

Install via CLI
npx skills add https://github.com/z3d/Starter-App-Dotnet-0 --skill ddd-implementation
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator