Examples

Commerce

A five-level fulfilment tree with mixed sequence scopes and transient request inputs.

A fulfilment test may need several orders and shipments, with numbering shared at different levels. This example separates those scopes and keeps checkout inputs unsaved so the code under test can process them.

Customer → Order → LineItem → Shipment → TrackingEvent, plus a transient CheckoutInput that models a request DTO. Each level numbers itself with a different scope. Source: tests/Zeeq.Biome.Tests/Commerce.

One seed, four scopes

SequenceScopeReads as
OrderNumbers.Per<Customer>()orders count up across a customer's whole history
LineNumbers.PerParent()each order starts its own line numbering
ShipmentNumbers.Per<Customer>()shipment numbers span all lines and orders for the customer
EventNumbers.PerParent()a shipment's checkpoints are ordered independently
Commerce/Domain.cs
// Shipments have line parents, but numbering spans
// all lines/orders for the customer.
internal static readonly Sequence<int> ShipmentNumbers = Sequence
    .Of(1, n => n + 1)
    .Per<Customer>();

// Leave Number/Reference unset: call-site overrides
// run before these fill-only derives.
internal static readonly ChildBlueprint<CommerceSeed, Customer, Order> Order =
    Blueprint
        .ChildOf<CommerceSeed, Customer, Order>(
            (customer, ctx) =>
                new()
                {
                    Customer = customer,
                    PlacedAt = ctx.Ago(TimeSpan.FromDays(2)),
                }
        )
        .Derive(o => o.Number, OrderNumbers)
        .Derive(o => o.Reference, (o, _) => $"ORDER-{o.Number}");

// Reach through Line -> Order -> Customer; relationships never depend on Last<T>().
internal static readonly ChildBlueprint<
    CommerceSeed,
    LineItem,
    Shipment
> Shipment = Blueprint
    .ChildOf<CommerceSeed, LineItem, Shipment>(
        (line, ctx) =>
            new()
            {
                Line = line,
                Customer = ctx.Ancestor<Customer>(),
                ExpectedAt = ctx.FromNow(TimeSpan.FromDays(1)),
            }
    )
    .Derive(s => s.CustomerNumber, ShipmentNumbers)
    .Derive(s => s.Label, (s, _) => $"PARCEL-{s.CustomerNumber}");

A busy store in one chain

Counts are per parent, so each line multiplies the previous output: 2 customers × 3 orders × 4 lines × 1 shipment × 3 events.

var biome = TestBiome.Start(out _, store);

await biome
    .AddCustomers(out var customers, 2)
    .AddOrders(out var orders, 3) // 6 orders
    .AddLineItems(out var lines, 4) // 24 lines
    .AddShipments(out var shipments, 1) // 24 shipments
    .AddTrackingEvents(out var events, 3) // 72 checkpoints
    .Under(orders) // sibling branch: checkout inputs hang off orders
    .AddCheckoutInputs(out var inputs, 1) // AsTransient lives on the blueprint
    .BuildAsync();
This setup takes 9 lines instead of 51 at the call site. The numbering rules remain visible in the blueprints: parcels count per customer, SKUs per order, and events within each shipment. Tests can reuse those choices without managing the counters themselves.

Assertions the real test makes: biome.All<Order>().Count == 6, lines[4].Number == 1 (a new order restarts), shipments[4].CustomerNumber == 5 (spans orders), shipments[12].CustomerNumber == 1 (a new customer restarts), and the store holds 2 + 6 + 24 + 24 + 72 rows, no CheckoutInput.

Transient request inputs

CheckoutInput is built next to the stored entities but never saved. AsTransient() lives on the blueprint, so every use is excluded:

Commerce/Domain.cs
// Request DTOs can be built alongside stored
// entities; transient is a blueprint variant.
internal static readonly ChildBlueprint<
    CommerceSeed,
    Order,
    CheckoutInput
> Checkout = Blueprint
    .ChildOf<CommerceSeed, Order, CheckoutInput>(
        (order, _) => new() { Order = order }
    )
    .Derive(i => i.Reference, (i, _) => i.Order.Reference)
    .AsTransient();

Traits, explicit numbers, resuming a customer

+ makes one order cancelled and priority; the comma makes another. The explicit Number = 1 is skipped by the generator, so the first order becomes 2, and .Under(customer) resumes the customer's sequence.

CommerceTests.cs
await biome
    .AddCustomer(out var customer)
    .AddOrders(
        out var orders,
        Orders.Cancelled + Orders.Priority,
        // Call site wins: 1 is explicit, so the first order skips to 2.
        o => o.Number = 1
    )
    .AddLineItems(out var lines, 2)
    .AddShipments(out var shipments, 1)
    .AddTrackingEvents(out _, 2)
    // Resume the customer's order sequence rather than starting from a leaf anchor.
    .Under(customer)
    .AddOrder(out var next)
    .BuildAsync();

// orders[0].Number == 2 ("ORDER-2"), orders[1].Number == 1, next.Number == 3
// lines[2].Number == 1 (local reset)
// shipments[2].CustomerNumber == 3 (customer-wide)

A helper for the deep default branch

Commerce/ScenarioHelpers.cs
public static class CommerceScenarioHelpers
{
    extension(BiomeBuilder<CommerceSeed, CommerceSeed> b)
    {
        public BiomeBuilder<CommerceSeed, TrackingEvent[]> AddTrackedOrder(
            out TrackingEvent leaf
        ) =>
            b.AddCustomer(out _)
                .AddOrder(out _)
                .AddLineItem(out _)
                .AddShipment(out _)
                .AddTrackingEvent(out leaf);
    }
}

The helper pairs with a shared clock to add a later checkpoint, as shown in Time and precision.