Part XII: End-to-End Walkthrough
What We Build
This chapter builds an e-commerce platform from an empty directory to a deployed application. Every command. Every file. Every generated artifact. By the end, we will have:
- 2 bounded contexts (Catalog, Ordering)
- 3 aggregates (Product, Order, Customer)
- 1 content type (BlogPost) with parts, blocks, and StreamField
- 3 admin modules (Products, Orders, BlogPosts)
- A page tree (Home, Catalog, Product Detail, Blog)
- An editorial workflow on BlogPost (Draft, Review, Translation, Published)
- 1 epic, 1 feature with 3 ACs, 1 specification interface, 1 domain service, 5 tests
- Full quality gates: analyzers, coverage, fuzz testing
The comparison at the end: approximately 300 lines written by the developer, approximately 15,000 lines generated by the compiler.
Step 1: Create the Project
cmf new MyStore --template ecommercecmf new MyStore --template ecommerceCreating MyStore from template 'ecommerce'...
Created: MyStore.sln
Created: Directory.Build.props
Created: .editorconfig
Created: src/MyStore.Requirements/
Created: src/MyStore.SharedKernel/
Created: src/MyStore.Specifications/
Created: src/MyStore.Lib/
Created: src/MyStore.Infrastructure.Postgres/
Created: src/MyStore.Server/
Created: src/MyStore.Client/
Created: test/MyStore.Tests/
Created: .github/workflows/build.yml
Done. Run 'dotnet build' to verify.Creating MyStore from template 'ecommerce'...
Created: MyStore.sln
Created: Directory.Build.props
Created: .editorconfig
Created: src/MyStore.Requirements/
Created: src/MyStore.SharedKernel/
Created: src/MyStore.Specifications/
Created: src/MyStore.Lib/
Created: src/MyStore.Infrastructure.Postgres/
Created: src/MyStore.Server/
Created: src/MyStore.Client/
Created: test/MyStore.Tests/
Created: .github/workflows/build.yml
Done. Run 'dotnet build' to verify.Project tree after Step 1:
MyStore/
├── MyStore.sln
├── Directory.Build.props
├── .editorconfig
├── src/
│ ├── MyStore.Requirements/
│ │ ├── MyStore.Requirements.csproj
│ │ ├── Epics/
│ │ └── Features/
│ ├── MyStore.SharedKernel/
│ │ ├── MyStore.SharedKernel.csproj
│ │ └── Result.cs
│ ├── MyStore.Specifications/
│ │ └── MyStore.Specifications.csproj
│ ├── MyStore.Lib/
│ │ ├── MyStore.Lib.csproj
│ │ ├── Catalog/
│ │ └── Ordering/
│ ├── MyStore.Infrastructure.Postgres/
│ │ └── MyStore.Infrastructure.Postgres.csproj
│ ├── MyStore.Server/
│ │ ├── MyStore.Server.csproj
│ │ └── Program.cs
│ └── MyStore.Client/
│ └── MyStore.Client.csproj
├── test/
│ └── MyStore.Tests/
│ └── MyStore.Tests.csproj
└── .github/
└── workflows/
└── build.ymlMyStore/
├── MyStore.sln
├── Directory.Build.props
├── .editorconfig
├── src/
│ ├── MyStore.Requirements/
│ │ ├── MyStore.Requirements.csproj
│ │ ├── Epics/
│ │ └── Features/
│ ├── MyStore.SharedKernel/
│ │ ├── MyStore.SharedKernel.csproj
│ │ └── Result.cs
│ ├── MyStore.Specifications/
│ │ └── MyStore.Specifications.csproj
│ ├── MyStore.Lib/
│ │ ├── MyStore.Lib.csproj
│ │ ├── Catalog/
│ │ └── Ordering/
│ ├── MyStore.Infrastructure.Postgres/
│ │ └── MyStore.Infrastructure.Postgres.csproj
│ ├── MyStore.Server/
│ │ ├── MyStore.Server.csproj
│ │ └── Program.cs
│ └── MyStore.Client/
│ └── MyStore.Client.csproj
├── test/
│ └── MyStore.Tests/
│ └── MyStore.Tests.csproj
└── .github/
└── workflows/
└── build.ymlStep 2: Define Requirements
Create the Epic
cmf add feature OrderFulfillment --epic DomainModelingEpiccmf add feature OrderFulfillment --epic DomainModelingEpicThe template already created DomainModelingEpic. We write the feature by hand in src/MyStore.Requirements/Features/OrderFulfillmentFeature.cs:
namespace MyStore.Requirements.Features;
using MyStore.Requirements;
using MyStore.Requirements.Epics;
/// <summary>
/// Feature: Order Fulfillment
/// Customers can place orders, cancel orders, and view order totals.
/// </summary>
public abstract record OrderFulfillmentFeature
: Feature<DomainModelingEpic>
{
public override string Title => "Order fulfillment and lifecycle";
public override RequirementPriority Priority
=> RequirementPriority.Critical;
public override string Owner => "ordering-team";
/// AC1: A customer can place an order with at least one line item
public abstract AcceptanceCriterionResult OrderCanBePlaced(
UserId customer, ProductId product, int quantity);
/// AC2: An order can be cancelled if not yet shipped
public abstract AcceptanceCriterionResult OrderCanBeCancelled(
UserId customer, OrderId order);
/// AC3: Order total equals sum of line totals
public abstract AcceptanceCriterionResult OrderTotalIsCorrect(
OrderId order, decimal expectedTotal);
}namespace MyStore.Requirements.Features;
using MyStore.Requirements;
using MyStore.Requirements.Epics;
/// <summary>
/// Feature: Order Fulfillment
/// Customers can place orders, cancel orders, and view order totals.
/// </summary>
public abstract record OrderFulfillmentFeature
: Feature<DomainModelingEpic>
{
public override string Title => "Order fulfillment and lifecycle";
public override RequirementPriority Priority
=> RequirementPriority.Critical;
public override string Owner => "ordering-team";
/// AC1: A customer can place an order with at least one line item
public abstract AcceptanceCriterionResult OrderCanBePlaced(
UserId customer, ProductId product, int quantity);
/// AC2: An order can be cancelled if not yet shipped
public abstract AcceptanceCriterionResult OrderCanBeCancelled(
UserId customer, OrderId order);
/// AC3: Order total equals sum of line totals
public abstract AcceptanceCriterionResult OrderTotalIsCorrect(
OrderId order, decimal expectedTotal);
}Build check:
dotnet builddotnet buildBuild succeeded.
warning REQ100: OrderFulfillmentFeature has 3 acceptance criteria but no
specification interface references it.
→ Create IOrderFulfillmentSpec with
[ForRequirement(typeof(OrderFulfillmentFeature))]Build succeeded.
warning REQ100: OrderFulfillmentFeature has 3 acceptance criteria but no
specification interface references it.
→ Create IOrderFulfillmentSpec with
[ForRequirement(typeof(OrderFulfillmentFeature))]The build succeeds, but the analyzer immediately tells us: the feature exists, but nothing specifies it yet.
Step 3: Create Specifications
Create src/MyStore.Specifications/IOrderFulfillmentSpec.cs:
namespace MyStore.Specifications;
using MyStore.Requirements.Features;
using MyStore.SharedKernel;
[ForRequirement(typeof(OrderFulfillmentFeature))]
public interface IOrderFulfillmentSpec
{
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBePlaced))]
Result PlaceOrder(User customer, Product product, int quantity);
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBeCancelled))]
Result CancelOrder(User customer, Order order);
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderTotalIsCorrect))]
Result VerifyOrderTotal(Order order, decimal expectedTotal);
}namespace MyStore.Specifications;
using MyStore.Requirements.Features;
using MyStore.SharedKernel;
[ForRequirement(typeof(OrderFulfillmentFeature))]
public interface IOrderFulfillmentSpec
{
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBePlaced))]
Result PlaceOrder(User customer, Product product, int quantity);
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBeCancelled))]
Result CancelOrder(User customer, Order order);
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderTotalIsCorrect))]
Result VerifyOrderTotal(Order order, decimal expectedTotal);
}Build check:
dotnet builddotnet buildBuild succeeded.
info REQ103: OrderFulfillmentFeature: all 3 ACs specified.
warning REQ200: IOrderFulfillmentSpec is not implemented by any class.
warning REQ300: OrderFulfillmentFeature has no test class.Build succeeded.
info REQ103: OrderFulfillmentFeature: all 3 ACs specified.
warning REQ200: IOrderFulfillmentSpec is not implemented by any class.
warning REQ300: OrderFulfillmentFeature has no test class.Progress: REQ100 is gone (spec exists). Now REQ200 (no implementation) and REQ300 (no tests) appear.
Step 4: Model the Domain
Create Aggregates
cmf add aggregate Product --context Catalog
cmf add aggregate Order --context Ordering
cmf add aggregate Customer --context Identitycmf add aggregate Product --context Catalog
cmf add aggregate Order --context Ordering
cmf add aggregate Customer --context IdentityDefine Properties and Compositions
Edit src/MyStore.Lib/Catalog/Product.cs:
namespace MyStore.Lib.Catalog;
[AggregateRoot("Product", BoundedContext = "Catalog")]
public partial class Product
{
[EntityId]
public partial ProductId Id { get; }
[Property("Name", Required = true, MaxLength = 200)]
public partial string Name { get; }
[Property("Sku", Required = true, MaxLength = 50)]
public partial string Sku { get; }
[Property("Description", MaxLength = 2000)]
public partial string? Description { get; }
[Composition]
public partial Money Price { get; }
[Property("StockQuantity", Required = true)]
public partial int StockQuantity { get; }
[Invariant("Product price must be positive")]
private Result PriceIsPositive()
=> Price.Amount > 0
? Result.Success()
: Result.Failure("Product price must be positive");
[Invariant("Stock cannot be negative")]
private Result StockNotNegative()
=> StockQuantity >= 0
? Result.Success()
: Result.Failure("Stock quantity cannot be negative");
}namespace MyStore.Lib.Catalog;
[AggregateRoot("Product", BoundedContext = "Catalog")]
public partial class Product
{
[EntityId]
public partial ProductId Id { get; }
[Property("Name", Required = true, MaxLength = 200)]
public partial string Name { get; }
[Property("Sku", Required = true, MaxLength = 50)]
public partial string Sku { get; }
[Property("Description", MaxLength = 2000)]
public partial string? Description { get; }
[Composition]
public partial Money Price { get; }
[Property("StockQuantity", Required = true)]
public partial int StockQuantity { get; }
[Invariant("Product price must be positive")]
private Result PriceIsPositive()
=> Price.Amount > 0
? Result.Success()
: Result.Failure("Product price must be positive");
[Invariant("Stock cannot be negative")]
private Result StockNotNegative()
=> StockQuantity >= 0
? Result.Success()
: Result.Failure("Stock quantity cannot be negative");
}Edit src/MyStore.Lib/Ordering/Order.cs:
namespace MyStore.Lib.Ordering;
[AggregateRoot("Order", BoundedContext = "Ordering")]
public partial class Order
{
[EntityId]
public partial OrderId Id { get; }
[Property("OrderDate", Required = true)]
public partial DateTime OrderDate { get; }
[Property("Status", Required = true)]
public partial OrderStatus Status { get; }
[Composition]
public partial IReadOnlyList<OrderLine> Lines { get; }
[Composition]
public partial ShippingAddress ShippingAddress { get; }
[Association]
public partial CustomerId CustomerId { get; }
[Invariant("Order must have at least one line item")]
private Result HasLines()
=> Lines.Count > 0
? Result.Success()
: Result.Failure("Order must have at least one line item");
[Invariant("Order total must be positive")]
private Result TotalIsPositive()
{
var total = Lines.Sum(l => l.LineTotal.Amount);
return total > 0
? Result.Success()
: Result.Failure($"Order total ({total}) must be positive");
}
[Invariant("Cancelled orders cannot be modified")]
private Result CancelledOrdersImmutable()
{
// This invariant is checked after any mutation
// If status is Cancelled, no changes should have been made
return Result.Success(); // Enforced by command handler guards
}
}
[Entity("OrderLine")]
public partial class OrderLine
{
[EntityId]
public partial OrderLineId Id { get; }
[Property("ProductName", Required = true, MaxLength = 200)]
public partial string ProductName { get; }
[Property("Quantity", Required = true)]
public partial int Quantity { get; }
[Composition]
public partial Money UnitPrice { get; }
[Composition]
public partial Money LineTotal { get; }
}
[ValueObject("Money")]
public partial class Money
{
[ValueComponent("Amount", "decimal", Required = true)]
public partial decimal Amount { get; }
[ValueComponent("Currency", "string", Required = true)]
public partial string Currency { get; }
}
[ValueObject("ShippingAddress")]
public partial class ShippingAddress
{
[ValueComponent("Street", "string", Required = true)]
public partial string Street { get; }
[ValueComponent("City", "string", Required = true)]
public partial string City { get; }
[ValueComponent("PostalCode", "string", Required = true)]
public partial string PostalCode { get; }
[ValueComponent("Country", "string", Required = true)]
public partial string Country { get; }
}
public enum OrderStatus
{
Draft, Placed, Paid, Shipped, Delivered, Cancelled
}namespace MyStore.Lib.Ordering;
[AggregateRoot("Order", BoundedContext = "Ordering")]
public partial class Order
{
[EntityId]
public partial OrderId Id { get; }
[Property("OrderDate", Required = true)]
public partial DateTime OrderDate { get; }
[Property("Status", Required = true)]
public partial OrderStatus Status { get; }
[Composition]
public partial IReadOnlyList<OrderLine> Lines { get; }
[Composition]
public partial ShippingAddress ShippingAddress { get; }
[Association]
public partial CustomerId CustomerId { get; }
[Invariant("Order must have at least one line item")]
private Result HasLines()
=> Lines.Count > 0
? Result.Success()
: Result.Failure("Order must have at least one line item");
[Invariant("Order total must be positive")]
private Result TotalIsPositive()
{
var total = Lines.Sum(l => l.LineTotal.Amount);
return total > 0
? Result.Success()
: Result.Failure($"Order total ({total}) must be positive");
}
[Invariant("Cancelled orders cannot be modified")]
private Result CancelledOrdersImmutable()
{
// This invariant is checked after any mutation
// If status is Cancelled, no changes should have been made
return Result.Success(); // Enforced by command handler guards
}
}
[Entity("OrderLine")]
public partial class OrderLine
{
[EntityId]
public partial OrderLineId Id { get; }
[Property("ProductName", Required = true, MaxLength = 200)]
public partial string ProductName { get; }
[Property("Quantity", Required = true)]
public partial int Quantity { get; }
[Composition]
public partial Money UnitPrice { get; }
[Composition]
public partial Money LineTotal { get; }
}
[ValueObject("Money")]
public partial class Money
{
[ValueComponent("Amount", "decimal", Required = true)]
public partial decimal Amount { get; }
[ValueComponent("Currency", "string", Required = true)]
public partial string Currency { get; }
}
[ValueObject("ShippingAddress")]
public partial class ShippingAddress
{
[ValueComponent("Street", "string", Required = true)]
public partial string Street { get; }
[ValueComponent("City", "string", Required = true)]
public partial string City { get; }
[ValueComponent("PostalCode", "string", Required = true)]
public partial string PostalCode { get; }
[ValueComponent("Country", "string", Required = true)]
public partial string Country { get; }
}
public enum OrderStatus
{
Draft, Placed, Paid, Shipped, Delivered, Cancelled
}Step 5: Generate Code
dotnet builddotnet build[Stage 0] Metamodel Registration
Registered 24 concepts across 6 DSLs
[Stage 1] DSL Validation & Collection
Found: Product (Catalog), Order (Ordering), Customer (Identity)
Validated: 0 errors, 0 warnings
[Stage 2] Core Generation
Generated: Product.g.cs (entity impl, backing fields)
Generated: ProductBuilder.g.cs (fluent builder)
Generated: ProductConfiguration.g.cs (EF Core)
Generated: IProductRepository.g.cs
Generated: ProductRepository.g.cs
Generated: Order.g.cs
Generated: OrderBuilder.g.cs
Generated: Order.Invariants.g.cs (EnsureInvariants)
Generated: OrderConfiguration.g.cs
Generated: OrderLineConfiguration.g.cs
Generated: IOrderRepository.g.cs
Generated: OrderRepository.g.cs
Generated: Customer.g.cs
Generated: CustomerBuilder.g.cs
Generated: CustomerConfiguration.g.cs
Generated: ICustomerRepository.g.cs
Generated: CustomerRepository.g.cs
Generated: AppDbContext.g.cs (3 DbSets)
Generated: RequirementRegistry.g.cs (1 feature, 3 ACs)
[Stage 3] Cross-Cutting Generation
Generated: ProductsController.g.cs (REST API)
Generated: OrdersController.g.cs
Generated: CustomersController.g.cs
[Stage 4] Traceability & Diagnostics
Generated: TraceabilityMatrix.g.cs
Build succeeded. 23 files generated.
warning REQ200: IOrderFulfillmentSpec is not implemented.
warning REQ300: OrderFulfillmentFeature has no test class.[Stage 0] Metamodel Registration
Registered 24 concepts across 6 DSLs
[Stage 1] DSL Validation & Collection
Found: Product (Catalog), Order (Ordering), Customer (Identity)
Validated: 0 errors, 0 warnings
[Stage 2] Core Generation
Generated: Product.g.cs (entity impl, backing fields)
Generated: ProductBuilder.g.cs (fluent builder)
Generated: ProductConfiguration.g.cs (EF Core)
Generated: IProductRepository.g.cs
Generated: ProductRepository.g.cs
Generated: Order.g.cs
Generated: OrderBuilder.g.cs
Generated: Order.Invariants.g.cs (EnsureInvariants)
Generated: OrderConfiguration.g.cs
Generated: OrderLineConfiguration.g.cs
Generated: IOrderRepository.g.cs
Generated: OrderRepository.g.cs
Generated: Customer.g.cs
Generated: CustomerBuilder.g.cs
Generated: CustomerConfiguration.g.cs
Generated: ICustomerRepository.g.cs
Generated: CustomerRepository.g.cs
Generated: AppDbContext.g.cs (3 DbSets)
Generated: RequirementRegistry.g.cs (1 feature, 3 ACs)
[Stage 3] Cross-Cutting Generation
Generated: ProductsController.g.cs (REST API)
Generated: OrdersController.g.cs
Generated: CustomersController.g.cs
[Stage 4] Traceability & Diagnostics
Generated: TraceabilityMatrix.g.cs
Build succeeded. 23 files generated.
warning REQ200: IOrderFulfillmentSpec is not implemented.
warning REQ300: OrderFulfillmentFeature has no test class.Project tree after Step 5:
MyStore/
├── src/
│ ├── MyStore.Requirements/
│ │ ├── Epics/DomainModelingEpic.cs
│ │ └── Features/OrderFulfillmentFeature.cs ← Written (12 lines)
│ ├── MyStore.SharedKernel/
│ │ └── Result.cs ← Template (15 lines)
│ ├── MyStore.Specifications/
│ │ └── IOrderFulfillmentSpec.cs ← Written (18 lines)
│ ├── MyStore.Lib/
│ │ ├── Catalog/
│ │ │ ├── Product.cs ← Written (25 lines)
│ │ │ └── obj/generated/
│ │ │ ├── Product.g.cs ← Generated
│ │ │ ├── ProductBuilder.g.cs ← Generated
│ │ │ └── ProductConfiguration.g.cs ← Generated
│ │ └── Ordering/
│ │ ├── Order.cs ← Written (70 lines)
│ │ └── obj/generated/
│ │ ├── Order.g.cs ← Generated
│ │ ├── Order.Invariants.g.cs ← Generated
│ │ ├── OrderBuilder.g.cs ← Generated
│ │ ├── OrderConfiguration.g.cs ← Generated
│ │ └── OrderLineConfiguration.g.cs ← Generated
│ ├── MyStore.Infrastructure.Postgres/
│ │ └── obj/generated/
│ │ ├── AppDbContext.g.cs ← Generated
│ │ ├── IProductRepository.g.cs ← Generated
│ │ ├── ProductRepository.g.cs ← Generated
│ │ ├── IOrderRepository.g.cs ← Generated
│ │ └── OrderRepository.g.cs ← Generated
│ └── MyStore.Server/
│ └── obj/generated/
│ ├── ProductsController.g.cs ← Generated
│ ├── OrdersController.g.cs ← Generated
│ └── CustomersController.g.cs ← Generated
└── test/
└── MyStore.Tests/ ← Empty (warnings)MyStore/
├── src/
│ ├── MyStore.Requirements/
│ │ ├── Epics/DomainModelingEpic.cs
│ │ └── Features/OrderFulfillmentFeature.cs ← Written (12 lines)
│ ├── MyStore.SharedKernel/
│ │ └── Result.cs ← Template (15 lines)
│ ├── MyStore.Specifications/
│ │ └── IOrderFulfillmentSpec.cs ← Written (18 lines)
│ ├── MyStore.Lib/
│ │ ├── Catalog/
│ │ │ ├── Product.cs ← Written (25 lines)
│ │ │ └── obj/generated/
│ │ │ ├── Product.g.cs ← Generated
│ │ │ ├── ProductBuilder.g.cs ← Generated
│ │ │ └── ProductConfiguration.g.cs ← Generated
│ │ └── Ordering/
│ │ ├── Order.cs ← Written (70 lines)
│ │ └── obj/generated/
│ │ ├── Order.g.cs ← Generated
│ │ ├── Order.Invariants.g.cs ← Generated
│ │ ├── OrderBuilder.g.cs ← Generated
│ │ ├── OrderConfiguration.g.cs ← Generated
│ │ └── OrderLineConfiguration.g.cs ← Generated
│ ├── MyStore.Infrastructure.Postgres/
│ │ └── obj/generated/
│ │ ├── AppDbContext.g.cs ← Generated
│ │ ├── IProductRepository.g.cs ← Generated
│ │ ├── ProductRepository.g.cs ← Generated
│ │ ├── IOrderRepository.g.cs ← Generated
│ │ └── OrderRepository.g.cs ← Generated
│ └── MyStore.Server/
│ └── obj/generated/
│ ├── ProductsController.g.cs ← Generated
│ ├── OrdersController.g.cs ← Generated
│ └── CustomersController.g.cs ← Generated
└── test/
└── MyStore.Tests/ ← Empty (warnings)Step 6: Add Content Types
Create src/MyStore.Lib/Content/BlogPost.cs:
namespace MyStore.Lib.Content;
[AggregateRoot("BlogPost", BoundedContext = "Content")]
[HasPart(typeof(RoutablePart))]
[HasPart(typeof(SeoablePart))]
[HasPart(typeof(TaggablePart))]
public partial class BlogPost
{
[EntityId]
public partial BlogPostId Id { get; }
[Property("Title", Required = true, MaxLength = 200)]
public partial string Title { get; }
[Property("Author", Required = true)]
public partial string Author { get; }
[Property("PublishedDate")]
public partial DateTime? PublishedDate { get; }
[StreamField("Body", AllowedBlocks = new[]
{
typeof(RichTextBlock),
typeof(ImageBlock),
typeof(CodeBlock),
typeof(QuoteBlock),
typeof(ProductCarouselBlock)
})]
public partial StreamFieldValue Body { get; }
[Invariant("Published posts must have a title")]
private Result PublishedHasTitle()
{
if (PublishedDate.HasValue && string.IsNullOrWhiteSpace(Title))
return Result.Failure("Published blog post must have a title");
return Result.Success();
}
}namespace MyStore.Lib.Content;
[AggregateRoot("BlogPost", BoundedContext = "Content")]
[HasPart(typeof(RoutablePart))]
[HasPart(typeof(SeoablePart))]
[HasPart(typeof(TaggablePart))]
public partial class BlogPost
{
[EntityId]
public partial BlogPostId Id { get; }
[Property("Title", Required = true, MaxLength = 200)]
public partial string Title { get; }
[Property("Author", Required = true)]
public partial string Author { get; }
[Property("PublishedDate")]
public partial DateTime? PublishedDate { get; }
[StreamField("Body", AllowedBlocks = new[]
{
typeof(RichTextBlock),
typeof(ImageBlock),
typeof(CodeBlock),
typeof(QuoteBlock),
typeof(ProductCarouselBlock)
})]
public partial StreamFieldValue Body { get; }
[Invariant("Published posts must have a title")]
private Result PublishedHasTitle()
{
if (PublishedDate.HasValue && string.IsNullOrWhiteSpace(Title))
return Result.Failure("Published blog post must have a title");
return Result.Success();
}
}Create src/MyStore.Lib/Content/Blocks/ProductCarouselBlock.cs:
namespace MyStore.Lib.Content.Blocks;
[ContentBlock("ProductCarousel")]
public partial class ProductCarouselBlock
{
[BlockField("Title", "string", MaxLength = 100)]
public partial string Title { get; }
[BlockField("ProductIds", "Guid[]")]
public partial Guid[] ProductIds { get; }
[BlockField("MaxItems", "int")]
public partial int MaxItems { get; }
}namespace MyStore.Lib.Content.Blocks;
[ContentBlock("ProductCarousel")]
public partial class ProductCarouselBlock
{
[BlockField("Title", "string", MaxLength = 100)]
public partial string Title { get; }
[BlockField("ProductIds", "Guid[]")]
public partial Guid[] ProductIds { get; }
[BlockField("MaxItems", "int")]
public partial int MaxItems { get; }
}Build:
dotnet builddotnet build[Stage 2] Core Generation
Generated: BlogPost.g.cs
Generated: BlogPostBuilder.g.cs
Generated: BlogPostConfiguration.g.cs (with part columns + JSON body)
Generated: ProductCarouselBlock.g.cs
[Stage 3] Cross-Cutting Generation
Generated: BlogPostsController.g.cs
Generated: ProductCarouselWidget.g.razor (Blazor WASM component)
Build succeeded. 29 files generated.[Stage 2] Core Generation
Generated: BlogPost.g.cs
Generated: BlogPostBuilder.g.cs
Generated: BlogPostConfiguration.g.cs (with part columns + JSON body)
Generated: ProductCarouselBlock.g.cs
[Stage 3] Cross-Cutting Generation
Generated: BlogPostsController.g.cs
Generated: ProductCarouselWidget.g.razor (Blazor WASM component)
Build succeeded. 29 files generated.Step 7: Build Admin Modules
Create src/MyStore.Lib/Admin/AdminModules.cs:
namespace MyStore.Lib.Admin;
using MyStore.Lib.Catalog;
using MyStore.Lib.Ordering;
using MyStore.Lib.Content;
[AdminModule("Products", Entity = typeof(Product),
ListFields = new[] { "Name", "Sku", "Price.Amount", "StockQuantity" },
FilterFields = new[] { "Name", "Sku" },
SortDefault = "Name")]
public partial class ProductsAdmin { }
[AdminModule("Orders", Entity = typeof(Order),
ListFields = new[] { "OrderDate", "Status", "CustomerId", "Lines.Count" },
FilterFields = new[] { "Status", "OrderDate" },
SortDefault = "-OrderDate")]
public partial class OrdersAdmin { }
[AdminModule("BlogPosts", Entity = typeof(BlogPost),
ListFields = new[] { "Title", "Author", "PublishedDate", "Tags" },
FilterFields = new[] { "Author", "Tags" },
SortDefault = "-PublishedDate")]
public partial class BlogPostsAdmin { }namespace MyStore.Lib.Admin;
using MyStore.Lib.Catalog;
using MyStore.Lib.Ordering;
using MyStore.Lib.Content;
[AdminModule("Products", Entity = typeof(Product),
ListFields = new[] { "Name", "Sku", "Price.Amount", "StockQuantity" },
FilterFields = new[] { "Name", "Sku" },
SortDefault = "Name")]
public partial class ProductsAdmin { }
[AdminModule("Orders", Entity = typeof(Order),
ListFields = new[] { "OrderDate", "Status", "CustomerId", "Lines.Count" },
FilterFields = new[] { "Status", "OrderDate" },
SortDefault = "-OrderDate")]
public partial class OrdersAdmin { }
[AdminModule("BlogPosts", Entity = typeof(BlogPost),
ListFields = new[] { "Title", "Author", "PublishedDate", "Tags" },
FilterFields = new[] { "Author", "Tags" },
SortDefault = "-PublishedDate")]
public partial class BlogPostsAdmin { }Build:
dotnet builddotnet build[Stage 3] Cross-Cutting Generation
Generated: ProductsAdminList.g.razor
Generated: ProductsAdminForm.g.razor
Generated: ProductsAdminDetail.g.razor
Generated: OrdersAdminList.g.razor
Generated: OrdersAdminForm.g.razor
Generated: OrdersAdminDetail.g.razor
Generated: BlogPostsAdminList.g.razor
Generated: BlogPostsAdminForm.g.razor (with StreamField editor)
Generated: BlogPostsAdminDetail.g.razor
Generated: AdminLayout.g.razor (sidebar with 3 modules)
Generated: AdminRoutes.g.cs
Build succeeded. 40 files generated.[Stage 3] Cross-Cutting Generation
Generated: ProductsAdminList.g.razor
Generated: ProductsAdminForm.g.razor
Generated: ProductsAdminDetail.g.razor
Generated: OrdersAdminList.g.razor
Generated: OrdersAdminForm.g.razor
Generated: OrdersAdminDetail.g.razor
Generated: BlogPostsAdminList.g.razor
Generated: BlogPostsAdminForm.g.razor (with StreamField editor)
Generated: BlogPostsAdminDetail.g.razor
Generated: AdminLayout.g.razor (sidebar with 3 modules)
Generated: AdminRoutes.g.cs
Build succeeded. 40 files generated.Step 8: Create Page Tree
Create src/MyStore.Lib/Pages/SitePages.cs:
namespace MyStore.Lib.Pages;
using MyStore.Lib.Catalog;
using MyStore.Lib.Content;
[Page("Home", Template = "home", IsRoot = true)]
[Zone("hero")]
[Zone("featured")]
[Zone("content")]
public partial class HomePage { }
[Page("Catalog", Template = "catalog", Parent = typeof(HomePage))]
[Zone("filters")]
[Zone("products")]
[BoundEntity(typeof(Product), UrlPattern = "/catalog/{Slug}")]
public partial class CatalogPage { }
[Page("Product Detail", Template = "product-detail",
Parent = typeof(CatalogPage))]
[Zone("main")]
[Zone("related")]
public partial class ProductDetailPage { }
[Page("Blog", Template = "blog", Parent = typeof(HomePage))]
[Zone("posts")]
[Zone("sidebar")]
[BoundEntity(typeof(BlogPost), UrlPattern = "/blog/{Slug}")]
public partial class BlogPage { }
// Widget placements
[WidgetPlacement(Page = typeof(HomePage), Zone = "hero",
Widget = typeof(HeroBannerWidget), Order = 1)]
[WidgetPlacement(Page = typeof(HomePage), Zone = "featured",
Widget = typeof(ProductCarouselBlock), Order = 1)]
[WidgetPlacement(Page = typeof(BlogPage), Zone = "sidebar",
Widget = typeof(TagCloudWidget), Order = 1)]
public partial class DefaultWidgetPlacements { }namespace MyStore.Lib.Pages;
using MyStore.Lib.Catalog;
using MyStore.Lib.Content;
[Page("Home", Template = "home", IsRoot = true)]
[Zone("hero")]
[Zone("featured")]
[Zone("content")]
public partial class HomePage { }
[Page("Catalog", Template = "catalog", Parent = typeof(HomePage))]
[Zone("filters")]
[Zone("products")]
[BoundEntity(typeof(Product), UrlPattern = "/catalog/{Slug}")]
public partial class CatalogPage { }
[Page("Product Detail", Template = "product-detail",
Parent = typeof(CatalogPage))]
[Zone("main")]
[Zone("related")]
public partial class ProductDetailPage { }
[Page("Blog", Template = "blog", Parent = typeof(HomePage))]
[Zone("posts")]
[Zone("sidebar")]
[BoundEntity(typeof(BlogPost), UrlPattern = "/blog/{Slug}")]
public partial class BlogPage { }
// Widget placements
[WidgetPlacement(Page = typeof(HomePage), Zone = "hero",
Widget = typeof(HeroBannerWidget), Order = 1)]
[WidgetPlacement(Page = typeof(HomePage), Zone = "featured",
Widget = typeof(ProductCarouselBlock), Order = 1)]
[WidgetPlacement(Page = typeof(BlogPage), Zone = "sidebar",
Widget = typeof(TagCloudWidget), Order = 1)]
public partial class DefaultWidgetPlacements { }Build:
dotnet builddotnet build[Stage 3] Cross-Cutting Generation
Generated: HomePageComponent.g.razor
Generated: CatalogPageComponent.g.razor
Generated: ProductDetailPageComponent.g.razor
Generated: BlogPageComponent.g.razor
Generated: PageRouter.g.cs (materialized path routing)
Generated: SitemapGenerator.g.cs
Build succeeded. 46 files generated.[Stage 3] Cross-Cutting Generation
Generated: HomePageComponent.g.razor
Generated: CatalogPageComponent.g.razor
Generated: ProductDetailPageComponent.g.razor
Generated: BlogPageComponent.g.razor
Generated: PageRouter.g.cs (materialized path routing)
Generated: SitemapGenerator.g.cs
Build succeeded. 46 files generated.Step 9: Set Up Workflow
Create src/MyStore.Lib/Workflows/EditorialWorkflow.cs:
namespace MyStore.Lib.Workflows;
using MyStore.Lib.Content;
[Workflow("Editorial", AttachedTo = typeof(BlogPost))]
[Stage("Draft", IsInitial = true)]
[Stage("Review")]
[Stage("Translation", ForEachLocale = true)]
[Stage("Published", IsFinal = true)]
[Transition("Draft", "Review",
Gate = "RequiresRole('editor')")]
[Transition("Review", "Translation",
Gate = "RequiresRole('reviewer')")]
[Transition("Translation", "Published",
Gate = "AllLocalesComplete AND RequiresRole('publisher')")]
[Transition("Review", "Draft",
Gate = "RequiresRole('reviewer')",
Label = "Reject")]
public partial class EditorialWorkflow { }namespace MyStore.Lib.Workflows;
using MyStore.Lib.Content;
[Workflow("Editorial", AttachedTo = typeof(BlogPost))]
[Stage("Draft", IsInitial = true)]
[Stage("Review")]
[Stage("Translation", ForEachLocale = true)]
[Stage("Published", IsFinal = true)]
[Transition("Draft", "Review",
Gate = "RequiresRole('editor')")]
[Transition("Review", "Translation",
Gate = "RequiresRole('reviewer')")]
[Transition("Translation", "Published",
Gate = "AllLocalesComplete AND RequiresRole('publisher')")]
[Transition("Review", "Draft",
Gate = "RequiresRole('reviewer')",
Label = "Reject")]
public partial class EditorialWorkflow { }Build:
dotnet builddotnet build[Stage 3] Cross-Cutting Generation
Generated: EditorialWorkflowStateMachine.g.cs
Generated: EditorialWorkflowGuards.g.cs
Generated: BlogPostWorkflowService.g.cs
Build succeeded. 49 files generated.[Stage 3] Cross-Cutting Generation
Generated: EditorialWorkflowStateMachine.g.cs
Generated: EditorialWorkflowGuards.g.cs
Generated: BlogPostWorkflowService.g.cs
Build succeeded. 49 files generated.Step 10: Implement Domain Service
Create src/MyStore.Lib/Ordering/Services/OrderFulfillmentService.cs:
namespace MyStore.Lib.Ordering.Services;
using MyStore.Specifications;
using MyStore.Requirements.Features;
using MyStore.SharedKernel;
[ForRequirement(typeof(OrderFulfillmentFeature))]
public class OrderFulfillmentService : IOrderFulfillmentSpec
{
private readonly IOrderRepository _orders;
private readonly IProductRepository _products;
public OrderFulfillmentService(
IOrderRepository orders,
IProductRepository products)
{
_orders = orders;
_products = products;
}
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBePlaced))]
public Result PlaceOrder(User customer, Product product, int quantity)
{
if (quantity <= 0)
return Result.Failure("Quantity must be positive");
if (product.StockQuantity < quantity)
return Result.Failure(
$"Insufficient stock: {product.StockQuantity} available, " +
$"{quantity} requested");
var line = new OrderLineBuilder()
.WithProductName(product.Name)
.WithQuantity(quantity)
.WithUnitPrice(product.Price)
.WithLineTotal(new Money(
product.Price.Amount * quantity,
product.Price.Currency))
.Build();
var order = new OrderBuilder()
.WithOrderDate(DateTime.UtcNow)
.WithStatus(OrderStatus.Placed)
.WithCustomer(customer.Id)
.AddLine(line.Value)
.Build();
return order.IsSuccess
? Result.Success()
: Result.Failure(order.Reason!);
}
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBeCancelled))]
public Result CancelOrder(User customer, Order order)
{
if (order.Status == OrderStatus.Shipped)
return Result.Failure("Cannot cancel a shipped order");
if (order.Status == OrderStatus.Delivered)
return Result.Failure("Cannot cancel a delivered order");
if (order.Status == OrderStatus.Cancelled)
return Result.Failure("Order is already cancelled");
// Domain logic: update status
return Result.Success();
}
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderTotalIsCorrect))]
public Result VerifyOrderTotal(Order order, decimal expectedTotal)
{
var actualTotal = order.Lines.Sum(l => l.LineTotal.Amount);
return actualTotal == expectedTotal
? Result.Success()
: Result.Failure(
$"Expected total {expectedTotal}, " +
$"actual total {actualTotal}");
}
}namespace MyStore.Lib.Ordering.Services;
using MyStore.Specifications;
using MyStore.Requirements.Features;
using MyStore.SharedKernel;
[ForRequirement(typeof(OrderFulfillmentFeature))]
public class OrderFulfillmentService : IOrderFulfillmentSpec
{
private readonly IOrderRepository _orders;
private readonly IProductRepository _products;
public OrderFulfillmentService(
IOrderRepository orders,
IProductRepository products)
{
_orders = orders;
_products = products;
}
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBePlaced))]
public Result PlaceOrder(User customer, Product product, int quantity)
{
if (quantity <= 0)
return Result.Failure("Quantity must be positive");
if (product.StockQuantity < quantity)
return Result.Failure(
$"Insufficient stock: {product.StockQuantity} available, " +
$"{quantity} requested");
var line = new OrderLineBuilder()
.WithProductName(product.Name)
.WithQuantity(quantity)
.WithUnitPrice(product.Price)
.WithLineTotal(new Money(
product.Price.Amount * quantity,
product.Price.Currency))
.Build();
var order = new OrderBuilder()
.WithOrderDate(DateTime.UtcNow)
.WithStatus(OrderStatus.Placed)
.WithCustomer(customer.Id)
.AddLine(line.Value)
.Build();
return order.IsSuccess
? Result.Success()
: Result.Failure(order.Reason!);
}
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBeCancelled))]
public Result CancelOrder(User customer, Order order)
{
if (order.Status == OrderStatus.Shipped)
return Result.Failure("Cannot cancel a shipped order");
if (order.Status == OrderStatus.Delivered)
return Result.Failure("Cannot cancel a delivered order");
if (order.Status == OrderStatus.Cancelled)
return Result.Failure("Order is already cancelled");
// Domain logic: update status
return Result.Success();
}
[ForRequirement(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderTotalIsCorrect))]
public Result VerifyOrderTotal(Order order, decimal expectedTotal)
{
var actualTotal = order.Lines.Sum(l => l.LineTotal.Amount);
return actualTotal == expectedTotal
? Result.Success()
: Result.Failure(
$"Expected total {expectedTotal}, " +
$"actual total {actualTotal}");
}
}Build:
dotnet builddotnet buildBuild succeeded. 49 files generated.
info REQ203: IOrderFulfillmentSpec: fully implemented by
OrderFulfillmentService with complete [ForRequirement] coverage.
warning REQ300: OrderFulfillmentFeature has no test class.Build succeeded. 49 files generated.
info REQ203: IOrderFulfillmentSpec: fully implemented by
OrderFulfillmentService with complete [ForRequirement] coverage.
warning REQ300: OrderFulfillmentFeature has no test class.REQ200 is gone (implementation exists). REQ300 remains (no tests yet).
Step 11: Write Tests
Create test/MyStore.Tests/Ordering/OrderFulfillmentTests.cs:
namespace MyStore.Tests.Ordering;
using MyStore.Requirements.Features;
using MyStore.Lib.Ordering.Services;
using MyStore.Lib.Catalog;
using MyStore.SharedKernel;
[TestFixture]
[TestsFor(typeof(OrderFulfillmentFeature))]
public class OrderFulfillmentTests
{
private OrderFulfillmentService _service;
private User _customer;
private Product _product;
[SetUp]
public void Setup()
{
_service = new OrderFulfillmentService(
new InMemoryOrderRepository(),
new InMemoryProductRepository());
_customer = new User(
new UserId(Guid.NewGuid(), "Alice", "Customer",
new Email("alice@example.com")),
"Alice",
new HashSet<Role>());
_product = new ProductBuilder()
.WithName("Laptop")
.WithSku("LAP-001")
.WithPrice(new Money(999.99m, "USD"))
.WithStockQuantity(10)
.Build().Value;
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBePlaced))]
public void Customer_can_place_order_with_valid_product()
{
var result = _service.PlaceOrder(_customer, _product, 2);
Assert.That(result.IsSuccess, Is.True);
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBePlaced))]
public void Cannot_place_order_with_zero_quantity()
{
var result = _service.PlaceOrder(_customer, _product, 0);
Assert.That(result.IsSuccess, Is.False);
Assert.That(result.Reason, Does.Contain("Quantity must be positive"));
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBeCancelled))]
public void Can_cancel_placed_order()
{
var order = CreatePlacedOrder();
var result = _service.CancelOrder(_customer, order);
Assert.That(result.IsSuccess, Is.True);
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBeCancelled))]
public void Cannot_cancel_shipped_order()
{
var order = CreateShippedOrder();
var result = _service.CancelOrder(_customer, order);
Assert.That(result.IsSuccess, Is.False);
Assert.That(result.Reason,
Does.Contain("Cannot cancel a shipped order"));
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderTotalIsCorrect))]
public void Order_total_matches_line_totals()
{
var order = CreateOrderWithLines(
new Money(10.00m, "USD"),
new Money(20.00m, "USD"));
var result = _service.VerifyOrderTotal(order, 30.00m);
Assert.That(result.IsSuccess, Is.True);
}
// Helper methods
private Order CreatePlacedOrder()
{
return new OrderBuilder()
.WithOrderDate(DateTime.UtcNow)
.WithStatus(OrderStatus.Placed)
.WithCustomer(_customer.Id)
.AddLine(new OrderLineBuilder()
.WithProductName("Laptop")
.WithQuantity(1)
.WithUnitPrice(new Money(999.99m, "USD"))
.WithLineTotal(new Money(999.99m, "USD"))
.Build().Value)
.Build().Value;
}
private Order CreateShippedOrder()
{
return new OrderBuilder()
.WithOrderDate(DateTime.UtcNow)
.WithStatus(OrderStatus.Shipped)
.WithCustomer(_customer.Id)
.AddLine(new OrderLineBuilder()
.WithProductName("Laptop")
.WithQuantity(1)
.WithUnitPrice(new Money(999.99m, "USD"))
.WithLineTotal(new Money(999.99m, "USD"))
.Build().Value)
.WithShippingAddress(new ShippingAddress(
"123 Main St", "Springfield", "62704", "US"))
.Build().Value;
}
private Order CreateOrderWithLines(params Money[] lineTotals)
{
var builder = new OrderBuilder()
.WithOrderDate(DateTime.UtcNow)
.WithStatus(OrderStatus.Placed)
.WithCustomer(_customer.Id);
foreach (var total in lineTotals)
{
builder.AddLine(new OrderLineBuilder()
.WithProductName("Item")
.WithQuantity(1)
.WithUnitPrice(total)
.WithLineTotal(total)
.Build().Value);
}
return builder.Build().Value;
}
}namespace MyStore.Tests.Ordering;
using MyStore.Requirements.Features;
using MyStore.Lib.Ordering.Services;
using MyStore.Lib.Catalog;
using MyStore.SharedKernel;
[TestFixture]
[TestsFor(typeof(OrderFulfillmentFeature))]
public class OrderFulfillmentTests
{
private OrderFulfillmentService _service;
private User _customer;
private Product _product;
[SetUp]
public void Setup()
{
_service = new OrderFulfillmentService(
new InMemoryOrderRepository(),
new InMemoryProductRepository());
_customer = new User(
new UserId(Guid.NewGuid(), "Alice", "Customer",
new Email("alice@example.com")),
"Alice",
new HashSet<Role>());
_product = new ProductBuilder()
.WithName("Laptop")
.WithSku("LAP-001")
.WithPrice(new Money(999.99m, "USD"))
.WithStockQuantity(10)
.Build().Value;
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBePlaced))]
public void Customer_can_place_order_with_valid_product()
{
var result = _service.PlaceOrder(_customer, _product, 2);
Assert.That(result.IsSuccess, Is.True);
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBePlaced))]
public void Cannot_place_order_with_zero_quantity()
{
var result = _service.PlaceOrder(_customer, _product, 0);
Assert.That(result.IsSuccess, Is.False);
Assert.That(result.Reason, Does.Contain("Quantity must be positive"));
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBeCancelled))]
public void Can_cancel_placed_order()
{
var order = CreatePlacedOrder();
var result = _service.CancelOrder(_customer, order);
Assert.That(result.IsSuccess, Is.True);
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderCanBeCancelled))]
public void Cannot_cancel_shipped_order()
{
var order = CreateShippedOrder();
var result = _service.CancelOrder(_customer, order);
Assert.That(result.IsSuccess, Is.False);
Assert.That(result.Reason,
Does.Contain("Cannot cancel a shipped order"));
}
[Test]
[Verifies(typeof(OrderFulfillmentFeature),
nameof(OrderFulfillmentFeature.OrderTotalIsCorrect))]
public void Order_total_matches_line_totals()
{
var order = CreateOrderWithLines(
new Money(10.00m, "USD"),
new Money(20.00m, "USD"));
var result = _service.VerifyOrderTotal(order, 30.00m);
Assert.That(result.IsSuccess, Is.True);
}
// Helper methods
private Order CreatePlacedOrder()
{
return new OrderBuilder()
.WithOrderDate(DateTime.UtcNow)
.WithStatus(OrderStatus.Placed)
.WithCustomer(_customer.Id)
.AddLine(new OrderLineBuilder()
.WithProductName("Laptop")
.WithQuantity(1)
.WithUnitPrice(new Money(999.99m, "USD"))
.WithLineTotal(new Money(999.99m, "USD"))
.Build().Value)
.Build().Value;
}
private Order CreateShippedOrder()
{
return new OrderBuilder()
.WithOrderDate(DateTime.UtcNow)
.WithStatus(OrderStatus.Shipped)
.WithCustomer(_customer.Id)
.AddLine(new OrderLineBuilder()
.WithProductName("Laptop")
.WithQuantity(1)
.WithUnitPrice(new Money(999.99m, "USD"))
.WithLineTotal(new Money(999.99m, "USD"))
.Build().Value)
.WithShippingAddress(new ShippingAddress(
"123 Main St", "Springfield", "62704", "US"))
.Build().Value;
}
private Order CreateOrderWithLines(params Money[] lineTotals)
{
var builder = new OrderBuilder()
.WithOrderDate(DateTime.UtcNow)
.WithStatus(OrderStatus.Placed)
.WithCustomer(_customer.Id);
foreach (var total in lineTotals)
{
builder.AddLine(new OrderLineBuilder()
.WithProductName("Item")
.WithQuantity(1)
.WithUnitPrice(total)
.WithLineTotal(total)
.Build().Value);
}
return builder.Build().Value;
}
}Build:
dotnet builddotnet buildBuild succeeded. 49 files generated.
info REQ103: OrderFulfillmentFeature: all 3 ACs specified.
info REQ203: IOrderFulfillmentSpec: fully implemented.
info REQ303: OrderFulfillmentFeature: all 3 ACs have [Verifies] tests.
OrderCanBePlaced: 2 tests
OrderCanBeCancelled: 2 tests
OrderTotalIsCorrect: 1 test
0 errors, 0 warnings.Build succeeded. 49 files generated.
info REQ103: OrderFulfillmentFeature: all 3 ACs specified.
info REQ203: IOrderFulfillmentSpec: fully implemented.
info REQ303: OrderFulfillmentFeature: all 3 ACs have [Verifies] tests.
OrderCanBePlaced: 2 tests
OrderCanBeCancelled: 2 tests
OrderTotalIsCorrect: 1 test
0 errors, 0 warnings.All REQ diagnostics are now green. Every AC is specified, implemented, and tested.
Step 12: Run Quality Gates
Run Tests
dotnet test --collect:"XPlat Code Coverage" --logger:trx \
--results-directory TestResultsdotnet test --collect:"XPlat Code Coverage" --logger:trx \
--results-directory TestResultsTest run for MyStore.Tests.dll (.NET 10.0)
Microsoft (R) Test Execution Engine
Starting test execution, please wait...
Passed! - Failed: 0, Passed: 5, Skipped: 0, Total: 5
Duration: 1.2 s
Results File: TestResults/MyStore.Tests_2026-03-19.trx
Attachments:
TestResults/.../coverage.cobertura.xmlTest run for MyStore.Tests.dll (.NET 10.0)
Microsoft (R) Test Execution Engine
Starting test execution, please wait...
Passed! - Failed: 0, Passed: 5, Skipped: 0, Total: 5
Duration: 1.2 s
Results File: TestResults/MyStore.Tests_2026-03-19.trx
Attachments:
TestResults/.../coverage.cobertura.xmlRun Quality Gates
dotnet quality-gates check \
--trx TestResults/*.trx \
--coverage TestResults/**/coverage.cobertura.xml \
--min-pass-rate 100 \
--min-coverage 80 \
--max-duration 5000 \
--fuzz-inputs 500dotnet quality-gates check \
--trx TestResults/*.trx \
--coverage TestResults/**/coverage.cobertura.xml \
--min-pass-rate 100 \
--min-coverage 80 \
--max-duration 5000 \
--fuzz-inputs 500Quality Gates Report
====================
FEATURE: OrderFulfillmentFeature (3 ACs, 5 tests)
[REQ400] Pass Rate
OrderCanBePlaced: 2/2 passed PASS
OrderCanBeCancelled: 2/2 passed PASS
OrderTotalIsCorrect: 1/1 passed PASS
[REQ401] AC Coverage
OrderCanBePlaced: covered PASS
OrderCanBeCancelled: covered PASS
OrderTotalIsCorrect: covered PASS
[REQ402] Code Coverage
OrderFulfillmentService.PlaceOrder: 92% PASS (>= 80%)
OrderFulfillmentService.CancelOrder: 88% PASS (>= 80%)
OrderFulfillmentService.VerifyOrderTotal: 100% PASS (>= 80%)
[REQ403] Duration
Customer_can_place_order: 18ms PASS (< 5000ms)
Cannot_place_order_zero_quantity: 4ms PASS
Can_cancel_placed_order: 12ms PASS
Cannot_cancel_shipped_order: 6ms PASS
Order_total_matches_line_totals: 8ms PASS
[REQ405] Fuzz Testing
OrderCanBePlaced: 500 inputs, 0 crashes PASS
478 Result.Success, 22 Result.Failure (expected)
OrderCanBeCancelled: 500 inputs, 0 crashes PASS
312 Result.Success, 188 Result.Failure (expected)
OrderTotalIsCorrect: 500 inputs, 0 crashes PASS
500 Result.Success, 0 Result.Failure
Summary
-------
OrderFulfillmentFeature: 5/5 gates passed
Overall: PASS (exit code 0)Quality Gates Report
====================
FEATURE: OrderFulfillmentFeature (3 ACs, 5 tests)
[REQ400] Pass Rate
OrderCanBePlaced: 2/2 passed PASS
OrderCanBeCancelled: 2/2 passed PASS
OrderTotalIsCorrect: 1/1 passed PASS
[REQ401] AC Coverage
OrderCanBePlaced: covered PASS
OrderCanBeCancelled: covered PASS
OrderTotalIsCorrect: covered PASS
[REQ402] Code Coverage
OrderFulfillmentService.PlaceOrder: 92% PASS (>= 80%)
OrderFulfillmentService.CancelOrder: 88% PASS (>= 80%)
OrderFulfillmentService.VerifyOrderTotal: 100% PASS (>= 80%)
[REQ403] Duration
Customer_can_place_order: 18ms PASS (< 5000ms)
Cannot_place_order_zero_quantity: 4ms PASS
Can_cancel_placed_order: 12ms PASS
Cannot_cancel_shipped_order: 6ms PASS
Order_total_matches_line_totals: 8ms PASS
[REQ405] Fuzz Testing
OrderCanBePlaced: 500 inputs, 0 crashes PASS
478 Result.Success, 22 Result.Failure (expected)
OrderCanBeCancelled: 500 inputs, 0 crashes PASS
312 Result.Success, 188 Result.Failure (expected)
OrderTotalIsCorrect: 500 inputs, 0 crashes PASS
500 Result.Success, 0 Result.Failure
Summary
-------
OrderFulfillmentFeature: 5/5 gates passed
Overall: PASS (exit code 0)All gates pass. The chain is complete.
Step 13: Deploy
# Build production image
dotnet publish src/MyStore.Server -c Release -o publish
# Or Docker
docker build -t mystore:latest .
docker run -p 8080:8080 mystore:latest# Build production image
dotnet publish src/MyStore.Server -c Release -o publish
# Or Docker
docker build -t mystore:latest .
docker run -p 8080:8080 mystore:latestMyStore.Server starting...
info: RequirementComplianceCheck[0]
── Requirement Compliance Report ──
info: RequirementComplianceCheck[0]
Order fulfillment and lifecycle: 3/3 ACs covered
info: RequirementComplianceCheck[0]
── All requirements verified ──
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:8080MyStore.Server starting...
info: RequirementComplianceCheck[0]
── Requirement Compliance Report ──
info: RequirementComplianceCheck[0]
Order fulfillment and lifecycle: 3/3 ACs covered
info: RequirementComplianceCheck[0]
── All requirements verified ──
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:8080The startup compliance check confirms all requirements are covered before the application accepts traffic.
Step 14: Review What Was Generated
Lines Written vs Lines Generated
Developer-Written Code
======================
MyStore.Requirements/
Epics/DomainModelingEpic.cs 8 lines
Features/OrderFulfillmentFeature.cs 18 lines
MyStore.SharedKernel/
Result.cs 15 lines
MyStore.Specifications/
IOrderFulfillmentSpec.cs 20 lines
MyStore.Lib/
Catalog/Product.cs 28 lines
Ordering/Order.cs 75 lines
Content/BlogPost.cs 32 lines
Content/Blocks/ProductCarouselBlock.cs 12 lines
Admin/AdminModules.cs 22 lines
Pages/SitePages.cs 30 lines
Workflows/EditorialWorkflow.cs 14 lines
Ordering/Services/OrderFulfillmentService.cs 55 lines
MyStore.Tests/
Ordering/OrderFulfillmentTests.cs 95 lines
────────────
TOTAL WRITTEN: ~324 lines
Generated Code (by dotnet build)
================================
Domain entities (backing fields, properties) ~600 lines
Fluent builders ~400 lines
EnsureInvariants() methods ~60 lines
EF Core configurations ~500 lines
EF Core DbContext ~100 lines
Repository interfaces ~120 lines
Repository implementations ~300 lines
REST API controllers ~600 lines
Blazor admin modules (list, form, detail) ~3,000 lines
Blazor page components ~800 lines
Blazor content block widgets ~400 lines
Page router + sitemap ~200 lines
Workflow state machines + guards ~300 lines
RequirementRegistry.g.cs ~50 lines
TraceabilityMatrix.g.cs ~80 lines
ForRequirement/Verifies/TestsFor attributes ~40 lines
GraphQL schema types ~400 lines
OpenAPI enrichment ~200 lines
────────────
TOTAL GENERATED: ~8,150 lines
Ratio: ~1:25 (324 written : 8,150 generated)Developer-Written Code
======================
MyStore.Requirements/
Epics/DomainModelingEpic.cs 8 lines
Features/OrderFulfillmentFeature.cs 18 lines
MyStore.SharedKernel/
Result.cs 15 lines
MyStore.Specifications/
IOrderFulfillmentSpec.cs 20 lines
MyStore.Lib/
Catalog/Product.cs 28 lines
Ordering/Order.cs 75 lines
Content/BlogPost.cs 32 lines
Content/Blocks/ProductCarouselBlock.cs 12 lines
Admin/AdminModules.cs 22 lines
Pages/SitePages.cs 30 lines
Workflows/EditorialWorkflow.cs 14 lines
Ordering/Services/OrderFulfillmentService.cs 55 lines
MyStore.Tests/
Ordering/OrderFulfillmentTests.cs 95 lines
────────────
TOTAL WRITTEN: ~324 lines
Generated Code (by dotnet build)
================================
Domain entities (backing fields, properties) ~600 lines
Fluent builders ~400 lines
EnsureInvariants() methods ~60 lines
EF Core configurations ~500 lines
EF Core DbContext ~100 lines
Repository interfaces ~120 lines
Repository implementations ~300 lines
REST API controllers ~600 lines
Blazor admin modules (list, form, detail) ~3,000 lines
Blazor page components ~800 lines
Blazor content block widgets ~400 lines
Page router + sitemap ~200 lines
Workflow state machines + guards ~300 lines
RequirementRegistry.g.cs ~50 lines
TraceabilityMatrix.g.cs ~80 lines
ForRequirement/Verifies/TestsFor attributes ~40 lines
GraphQL schema types ~400 lines
OpenAPI enrichment ~200 lines
────────────
TOTAL GENERATED: ~8,150 lines
Ratio: ~1:25 (324 written : 8,150 generated)In a full-featured application with more aggregates, content types, and requirements, this ratio approaches 1:50. The developer writes the domain model and the requirements. The compiler writes everything else.
Generated Artifacts Flow
Final Project Structure
End-to-End Sequence
Summary
This walkthrough demonstrated the full CMF lifecycle:
| Step | Action | Lines Written | Artifacts |
|---|---|---|---|
| 1 | cmf new |
0 | Solution scaffold |
| 2 | Define requirements | 18 | 1 feature, 3 ACs |
| 3 | Create specifications | 20 | 1 interface |
| 4 | Model domain | 135 | 3 aggregates, entities, VOs |
| 5 | dotnet build |
0 | 23 generated files |
| 6 | Add content types | 44 | BlogPost + blocks |
| 7 | Build admin | 22 | 3 admin modules |
| 8 | Create pages | 30 | 4 pages, widgets |
| 9 | Set up workflow | 14 | Editorial workflow |
| 10 | Implement service | 55 | OrderFulfillmentService |
| 11 | Write tests | 95 | 5 type-linked tests |
| 12 | Quality gates | 0 | All gates pass |
| 13 | Deploy | 0 | Production binary |
| Total | ~324 lines | ~8,150 generated |
The developer wrote the "what" (requirements, domain model, content types). The compiler wrote the "how" (entities, persistence, API, admin, pages, workflow engine, traceability). At every step, the analyzers guided the developer: missing specification, missing implementation, missing tests. The quality gates verified that tests pass, coverage meets thresholds, and fuzz testing found no crashes.
Three hundred and twenty-four lines of intent. Eight thousand one hundred and fifty lines of infrastructure. One compiler. Zero strings. The chain is complete.