Domain Develop
project-spec -> architecture-design -> domain-develop -> application-develop -> adapter-develop -> observability-develop -> test-develop
Prerequisites
Section titled “Prerequisites”- If
00-project-spec.mdgenerated by theproject-specskill exists, it is automatically read to confirm Aggregate candidates and business rules. - If
01-architecture-design.mdgenerated by thearchitecture-designskill exists, it is read to confirm folder structure and naming conventions. - If prerequisite documents are missing, the user is asked directly.
Background
Section titled “Background”When writing domain code following DDD tactical design, there are many repeating patterns. The Create/Validate/CreateFromValidated triple factory for Value Objects, event publishing for Aggregate Roots, the Fin<Unit> return pattern for command methods — all follow identical structures per building block.
The /domain-develop skill automates this repetition. When you convey domain requirements in natural language, it generates code, unit tests, and documentation matching Functorium framework patterns in 5 steps.
Skill Overview
Section titled “Skill Overview”5-Step Process
Section titled “5-Step Process”| Step | Task | Deliverable |
|---|---|---|
| 1 | Requirements analysis | Domain model analysis table, folder structure |
| 2 | Code generation | VO, Entity, Aggregate, Event, Error, Spec, Service |
| 3 | Unit test generation | T1_T2_T3 naming convention, Shouldly verification |
| 4 | Build/test verification | dotnet build + dotnet test passing |
| 5 | Document generation (optional) | Markdown design documents |
Supported Building Blocks
Section titled “Supported Building Blocks”| Building Block | Base Class | Description |
|---|---|---|
| Simple Value Object | SimpleValueObject<T> | Single primitive value wrapping |
| Composite Value Object | ValueObject | Multiple VO combination |
| Union Value Object | UnionValueObject / UnionValueObject<TSelf> | Allowed state combinations, state transitions |
| Entity | Entity<TId> | Child entity within an Aggregate |
| Aggregate Root | AggregateRoot<TId> | Transaction boundary |
| Domain Event | DomainEvent | State change notification |
| Domain Error | DomainErrorType.Custom | Business rule violation |
| Specification | ExpressionSpecification<T> | Query/search conditions |
| Domain Service | IDomainService | Cross-Aggregate pure logic |
| Repository | IRepository<T, TId> | Persistence interface |
Core API Patterns
Section titled “Core API Patterns”| Pattern | Usage |
|---|---|
Fin<T> success/failure | result.IsSucc, result.IsFail |
| Value extraction | result.ThrowIfFail() |
| Success return | unit (using static LanguageExt.Prelude;) |
| Failure return | DomainError.For<T>(new ErrorRecord(), id, message) |
| EntityId generation | {Type}Id.New() (Ulid-based) |
| Parallel validation composition | (Fin1, Fin2).Apply((v1, v2) => ...) |
| State transition | TransitionFrom<TFrom, TTo>(mapper) |
Entity Collection Management
Section titled “Entity Collection Management”When managing Child Entity collections within an Aggregate, use the IReadOnlyList<T> public + List<T> internal pattern.
private readonly List<OrderItem> _items = new();public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();
public Fin<Unit> AddItem(OrderItem item){ _items.Add(item); AddDomainEvent(new ItemAddedEvent(Id, item.Id)); return unit;}
public Fin<Unit> RemoveItem(OrderItemId itemId){ var item = _items.FirstOrDefault(i => i.Id == itemId); if (item is null) return unit; // Idempotency: removing a non-existent item succeeds _items.Remove(item); AddDomainEvent(new ItemRemovedEvent(Id, itemId)); return unit;}CtxEnricher Attributes
Section titled “CtxEnricher Attributes”Observability propagation attributes are applied to Request/Response DTOs in the Application layer. These attributes reflect the ctx.* propagation strategy designed in the observability-develop skill into code.
| Attribute | Purpose | Example |
|---|---|---|
[CtxRoot] | Promote to ctx.{field} root level | [CtxRoot] string CustomerId |
[CtxTarget(CtxPillar.All)] | Propagate to specific Pillar | [CtxTarget(CtxPillar.All)] string CustomerTier |
[CtxIgnore] | Exclude from all Pillars | [CtxIgnore] string InternalMemo |
Basic Invocation
Section titled “Basic Invocation”/domain-develop Product aggregate with ProductName (max 100 chars), ProductPrice (positive decimal)Interactive Mode
Section titled “Interactive Mode”Invoking /domain-develop without arguments starts the skill in interactive mode, collecting requirements through conversation.
Execution Flow
Section titled “Execution Flow”- Present analysis results — Shows identification results of Aggregates, Value Objects, Events, Errors, etc. in a table
- User confirmation — Proceed to code generation after confirming the analysis results
- Code + test generation — Generates code and tests per building block
- Build/test verification — Runs
dotnet buildanddotnet testto confirm passing
Example 1: Basic — Aggregate and Value Object
Section titled “Example 1: Basic — Aggregate and Value Object”This is the most basic Aggregate pattern. It creates two Simple Value Objects wrapping single primitive values and an Aggregate Root that has them as properties. This structure is the starting point for Functorium domain development.
Prompt
Section titled “Prompt”/domain-develop Product aggregate with ProductName (max 100 chars), ProductPrice (positive decimal).Create, UpdateName, UpdatePrice command methodsExpected Results
Section titled “Expected Results”| Building Block | Type | Description |
|---|---|---|
| Simple VO | ProductName | SimpleValueObject<string>, 100 char limit |
| Simple VO | ProductPrice | SimpleValueObject<decimal>, positive validation |
| Aggregate | Product | AggregateRoot<ProductId>, 3 command methods |
| Domain Event | CreatedEvent, NameUpdatedEvent, PriceUpdatedEvent | State change notification |
| Unit Tests | ~36 | VO validation + Aggregate commands + FinApply |
Key Snippets
Section titled “Key Snippets”Simple Value Object — Single primitive value wrapping, Create/Validate/CreateFromValidated triple factory:
public sealed class ProductName : SimpleValueObject<string>{ public const int MaxLength = 100;
private ProductName(string value) : base(value) { }
public static Fin<ProductName> Create(string? value) => CreateFromValidation(Validate(value), v => new ProductName(v));
public static Validation<Error, string> Validate(string? value) => ValidationRules<ProductName> .NotNull(value) .ThenNotEmpty() .ThenMaxLength(MaxLength) .ThenNormalize(v => v.Trim());}Command method — Fin<Unit> return, state change + event publishing:
public Fin<Unit> UpdateName(ProductName newName, DateTime now){ Name = newName; UpdatedAt = now; AddDomainEvent(new NameUpdatedEvent(Id, newName)); return unit;}Example 2: Intermediate — Union Type and Child Entity
Section titled “Example 2: Intermediate — Union Type and Child Entity”This adds three patterns to Example 1. Union Value Object that encodes state transitions as types, Composite Value Object that combines multiple VOs, and Child Entity that belongs within an Aggregate. When these three patterns combine, rules like “only Pending orders can be confirmed” or “shipping address can only be created when all 4 fields are valid” can be guaranteed at compile time.
Prompt
Section titled “Prompt”/domain-develop Order aggregate with OrderStatus union(Pending -> Confirmed -> Shipped -> Cancelled state transitions),ShippingAddress composite VO (Street, City, State, ZipCode),OrderItem child entity (ProductName, Quantity, UnitPrice).Confirm and Ship command methodsExpected Results
Section titled “Expected Results”| Building Block | Type | Description |
|---|---|---|
| Union VO | OrderStatus | UnionValueObject<OrderStatus>, 4 states, transition methods |
| Composite VO | ShippingAddress | ValueObject, 4 sub-VO combination |
| Child Entity | OrderItem | Entity<OrderItemId>, managed through parent Aggregate |
| Aggregate | Order | AggregateRoot<OrderId>, state transition-based commands |
Key Snippets
Section titled “Key Snippets”Union Value Object state transition — TransitionFrom allows only permitted transitions, others return InvalidTransition error:
[UnionType]public abstract partial record OrderStatus : UnionValueObject<OrderStatus>{ public sealed record Pending(DateTime CreatedAt) : OrderStatus; public sealed record Confirmed(DateTime ConfirmedAt) : OrderStatus; public sealed record Shipped(DateTime ShippedAt) : OrderStatus; public sealed record Cancelled(DateTime CancelledAt) : OrderStatus;
private OrderStatus() { }
public Fin<Confirmed> Confirm(DateTime now) => TransitionFrom<Pending, Confirmed>( _ => new Confirmed(now));
public Fin<Shipped> Ship(DateTime now) => TransitionFrom<Confirmed, Shipped>( _ => new Shipped(now));}Composite Value Object — Parallel composition of child VO Validate via .Apply() to accumulate errors:
public sealed class ShippingAddress : ValueObject{ public Street Street { get; } public City City { get; } public State State { get; } public ZipCode ZipCode { get; }
private ShippingAddress(Street street, City city, State state, ZipCode zipCode) { Street = street; City = city; State = state; ZipCode = zipCode; }
protected override IEnumerable<object> GetEqualityComponents() { yield return Street; yield return City; yield return State; yield return ZipCode; }
public static Validation<Error, (string, string, string, string)> Validate( string? street, string? city, string? state, string? zipCode) => (Street.Validate(street), City.Validate(city), State.Validate(state), ZipCode.Validate(zipCode)) .Apply((s, c, st, z) => (s, c, st, z));
public static Fin<ShippingAddress> Create( string? street, string? city, string? state, string? zipCode) => CreateFromValidation<ShippingAddress, (string, string, string, string)>( Validate(street, city, state, zipCode), v => new ShippingAddress( Street.CreateFromValidated(v.Item1), City.CreateFromValidated(v.Item2), State.CreateFromValidated(v.Item3), ZipCode.CreateFromValidated(v.Item4)));}Child Entity — Inherits Entity<TId>, managed through parent Aggregate without event publishing:
[GenerateEntityId]public sealed class OrderItem : Entity<OrderItemId>{ public ProductName ProductName { get; } public Quantity Quantity { get; private set; } public UnitPrice UnitPrice { get; }
private OrderItem(OrderItemId id, ProductName name, Quantity qty, UnitPrice price) : base(id) { ProductName = name; Quantity = qty; UnitPrice = price; }
public static OrderItem Create(ProductName name, Quantity qty, UnitPrice price) => new(OrderItemId.New(), name, qty, price);}Example 3: Advanced — Domain Service and Specification
Section titled “Example 3: Advanced — Domain Service and Specification”Logic that cannot be resolved by a single Aggregate emerges. Specification encapsulates query conditions as objects, and Domain Service contains pure logic spanning multiple Aggregates. These two patterns handle cross-cutting concerns while respecting Aggregate boundaries.
Prompt
Section titled “Prompt”/domain-develop Inventory aggregate with StockQuantity VO (non-negative int),LowStockThreshold VO (positive int).InventoryLowStockSpec specification (stock at or below threshold),InventoryTransferService domain service (source -> target stock transfer).Restock, Transfer command methodsExpected Results
Section titled “Expected Results”| Building Block | Type | Description |
|---|---|---|
| Simple VO | StockQuantity | SimpleValueObject<int>, 0 or greater |
| Simple VO | LowStockThreshold | SimpleValueObject<int>, positive |
| Specification | InventoryLowStockSpec | ExpressionSpecification<Inventory>, stock <= threshold |
| Domain Service | InventoryTransferService | IDomainService, returns failure on insufficient stock |
| Aggregate | Inventory | AggregateRoot<InventoryId>, Restock/Deduct commands |
Key Snippets
Section titled “Key Snippets”Specification — Converts VO to primitive to capture in Expression closure, enabling EF Core query translation:
public sealed class InventoryLowStockSpec : ExpressionSpecification<Inventory>{ public LowStockThreshold Threshold { get; }
public InventoryLowStockSpec(LowStockThreshold threshold) => Threshold = threshold;
public override Expression<Func<Inventory, bool>> ToExpression() { int thresholdValue = Threshold; return inventory => inventory.StockQuantity <= thresholdValue; }}Domain Service — Stateless pure function, expressing cross-Aggregate logic as Fin<Unit>:
public sealed class InventoryTransferService : IDomainService{ public sealed record InsufficientStock : DomainErrorType.Custom;
public Fin<Unit> Transfer( Inventory source, Inventory target, StockQuantity amount, DateTime now) { if (source.StockQuantity < amount) return DomainError.For<InventoryTransferService>( new InsufficientStock(), source.Id.ToString(), $"Insufficient stock: current {(int)source.StockQuantity}, requested {(int)amount}");
source.Deduct(amount, now); target.Restock(amount, now); return unit; }}Example 4: Practical — Complete Contact Domain Implementation
Section titled “Example 4: Practical — Complete Contact Domain Implementation”All patterns from the previous three examples are combined in a single domain. The business requirements from the Designing with Types example are passed directly as a prompt to auto-generate the complete domain model. 9 Value Objects, 2 Unions, 1 Child Entity, 1 Aggregate, 1 Domain Service, 1 Specification — plus 114 unit tests, all created with a single skill invocation.
Prompt
Section titled “Prompt”/domain-develop Contact aggregate -- implement the contact management domain.
## Contact Information Structure- Personal name: First Name (required), Last Name (required), Middle Initial (optional)- Email address: Standard email format- Postal address: Address (Street), City, State Code (2 uppercase letters), Zip Code (5-digit number)- Notes: Free-form text, 500 characters or less
## Business Rules1. Data validity: First/last name 50 chars or less, email standard format, state code 2 uppercase letters, zip code 5-digit number2. Contact method: At least one required (email only / postal address only / both). A contact without any contact method cannot exist3. Email verification: Unverified -> Verified one-way transition. Verification timestamp recorded. Re-verification of already verified email is not allowed4. Contact lifecycle: Name change, soft delete (deleter + timestamp), restoration possible. Modifying a deleted contact is not allowed. Delete/restore are idempotent5. Note management: Add/remove possible, not allowed for deleted contacts, removing a non-existent note is idempotent6. Email uniqueness: No duplicates allowed, excluding self
## Impossible States- Verified status without an email- Contact without any contact method- Verified -> Unverified reversal- Performing actions on a deleted contact- Duplicate contacts with the same emailExpected Results
Section titled “Expected Results”This prompt generates the same domain model as the Designing with Types example:
| Building Block | Type | Description |
|---|---|---|
| Simple VO (9) | FirstName, LastName, MiddleInitial, EmailAddress, Street, City, StateCode, ZipCode, NoteContent | Primitive value validation |
| Composite VO (2) | PersonalName, PostalAddress | VO combination |
| Union VO (2) | ContactInfo (email only/postal only/both), EmailStatus (unverified/verified) | Allowed state combinations, one-way transition |
| Child Entity | ContactNote | Note management |
| Aggregate | Contact | Create, UpdateName, VerifyEmail, SoftDelete, Restore, AddNote, RemoveNote |
| Domain Service | ContactEmailUniquenessService | Email uniqueness verification |
| Specification | ContactByEmailSpec | Email-based query |
| Unit Tests | ~114 | 6 business rule groups + 10 scenarios fully verified |
Why This Example Matters
Section titled “Why This Example Matters”This example is not simple CRUD. The rule “a contact without any contact method cannot exist” is encoded as a Union type, “verification is one-way” as a state transition, and “modifying a deleted contact is not allowed” as an Aggregate guard condition — each encoded into the type system. To see how invalid states are blocked at compile time, refer to the full design process in the Designing with Types example.
References
Section titled “References”Workflow
Section titled “Workflow”- Workflow — 7-step overall flow
- Project Spec Skill — Previous step: PRD writing
- Architecture Design Skill — Previous step: Project structure design
- Application Develop Skill — Next step: Use case implementation
Framework Guides
Section titled “Framework Guides”- DDD Tactical Design Overview
- Value Objects
- Value Object Validation
- Union Value Objects
- Aggregate Design
- Entity/Aggregate Core
- Domain Events
- Error System
- Specification
- Domain Services
- Unit Testing
Practical Examples
Section titled “Practical Examples”- Designing with Types — Complete contact domain design/implementation process