In enterprise multi-tenant software, delivering new environments, automated CI test suites, and day-to-day local development rely heavily on database baseline state: platform-standard ISO currencies, system roles and permissions, demo law firms, and sample litigation matters must be populated reliably and idempotently. Traditional ad-hoc SQL scripts carry critical liabilities: repeated executions throw primary key constraint violations, fail to express inter-module dependency topologies, and cannot prevent demo artifacts from polluting production databases.
BitzOrcas.Modern establishes a declarative, tiered seeding architecture powered by ISeedStep and ISeedRunner:
- Unified Step Contract (
ISeedStep): Each module declares its own seed steps, providing a globally uniqueSeedId, execution priorityOrder, monotonicVersion, and explicitDependsOnprerequisites; - Four-Tier Environment Scopes (
SeedScope): Explicitly segregatesGlobal(universal platform baselines),Tenant(default tenant settings),Demo(sample trial data), andProductionSafe(additive-only production changes).Demoseeds are automatically blocked in Staging and Production; - Topological DAG Resolution: At boot time,
ISeedRunnerevaluates the dependency graph across all registered steps to guarantee parent foreign keys are populated before dependent records; - Unified Initialization CLI: Running
dotnet run --project src/Hosts/BitzOrcas.Api -- --init-schemaexecutes schema migrations followed by ordered seed steps.
This guide demonstrates implementing and registering “Sample Civil Litigation Matters (LegalDemoMattersSeedStep)” within the Legal domain module (BitzOrcas.Modules.Legal).
Seed Data Tiering & Pipeline Execution Flow
Step 1: Implement the Module-Level ISeedStep Contract
Implement ISeedStep inside the module’s infrastructure layer. Inject the command repository for idempotent persistence and assign SeedScope.Demo to ensure non-production isolation:
using System;using System.Threading;using System.Threading.Tasks;using BitzOrcas.Domain.Abstractions;using BitzOrcas.Domain.Results;using BitzOrcas.Infrastructure.Seeders;using BitzOrcas.Modules.Legal.Domain;using Microsoft.Extensions.Logging;
namespace BitzOrcas.Modules.Legal.Seeders;
/// <summary>/// Sample litigation matters seed step/// </summary>/// <remarks>/// <para>Idempotently populates sample litigation matter dossiers in development and demo environments.</para>/// <para>Depends on <c>identity_roles</c> to ensure designated lead counsel users exist.</para>/// </remarks>public sealed class LegalDemoMattersSeedStep : ISeedStep{ private readonly ICommandRepository<MatterIntake, string> _repository; private readonly ILogger<LegalDemoMattersSeedStep> _logger;
/// <summary> /// Initializes sample matters seed step /// </summary> /// <param name="repository">Write-side command repository.</param> /// <param name="logger">Structured logger.</param> public LegalDemoMattersSeedStep( ICommandRepository<MatterIntake, string> repository, ILogger<LegalDemoMattersSeedStep> logger) { _repository = repository; _logger = logger; }
/// <summary> /// Execution order: 300+ represents business demo tiers, executed after infrastructure and identity roles /// </summary> public int Order => 320;
/// <summary> /// Globally unique seed identifier (snake_case recommended) /// </summary> public string SeedId => "legal_demo_matters";
/// <summary> /// Scope classification: Demo seeds are automatically skipped in Staging and Production /// </summary> public SeedScope SeedScope => SeedScope.Demo;
/// <summary> /// Monotonic version number for tracking schema drift /// </summary> public int Version => 1;
/// <summary> /// List of prerequisite SeedIds that must execute prior to this step /// </summary> public string[] DependsOn => new[] { "identity_platform_tenants", "identity_roles" };
/// <summary> /// Executes idempotent data seeding asynchronously /// </summary> public async Task ExecuteAsync(string environment, CancellationToken cancellationToken) { const string targetMatterId = "MAT-DEMO-2026-0001";
// 1. Strict idempotency pre-check: if target entity already exists, skip execution var existing = await _repository.FindAsync(targetMatterId, cancellationToken); if (existing.IsSuccess) { _logger.LogInformation("[LegalDemoMattersSeedStep] Sample matter {MatterId} already exists; skipping.", targetMatterId); return; }
// 2. Instantiate demo aggregate root via domain factory method var demoMatter = MatterIntake.Create( id: targetMatterId, tenantId: "tenant-alpha-lawfirm", matterCode: "CIV-2026-0081", title: "Sample Litigation: Semiconductor Intellectual Property Dispute", clientId: "CLI-DEMO-001", leadLawyerId: "USR-LAWYER-01", claimAmount: 12000000.00m);
if (demoMatter.IsFailure) { _logger.LogError("[LegalDemoMattersSeedStep] Failed to construct sample matter: {Error}", demoMatter.Error.Message); return; }
// 3. Persist aggregate root var saveResult = await _repository.SaveAsync(demoMatter.Value, cancellationToken); if (saveResult.IsSuccess) { _logger.LogInformation("[LegalDemoMattersSeedStep] Successfully seeded sample matter: {MatterId}", targetMatterId); } else { _logger.LogError("[LegalDemoMattersSeedStep] Failed to persist sample matter: {Error}", saveResult.Error.Message); } }}Step 2: Register SeedStep in Dependency Injection
Register the step as ISeedStep within module DI registration extensions:
using BitzOrcas.Infrastructure.Seeders;using BitzOrcas.Modules.Legal.Seeders;using Microsoft.Extensions.DependencyInjection;
namespace BitzOrcas.Modules.Legal;
public static class LegalModuleExtensions{ public static IServiceCollection AddLegalModuleSeeders(this IServiceCollection services) { // Register seed step for automatic discovery and DAG sorting by ISeedRunner services.AddSingleton<ISeedStep, LegalDemoMattersSeedStep>(); return services; }}Step 3: Execute Schema Initialization and Seeding
Run database schema migration and ordered seeding via the API Host CLI:
# 1. Production safe mode: migrates tables and populates Global, Tenant, and ProductionSafe seedsdotnet run --project src/Hosts/BitzOrcas.Api -- --init-schema
# 2. Schema migration only (skips all seeding steps)dotnet run --project src/Hosts/BitzOrcas.Api -- --init-schema --no-seed
# 3. Development environment reset: updates schema and re-seeds demo passwordsUSER__ADMIN__PASSWORD="YourStrongPassword123!" dotnet run --project src/Hosts/BitzOrcas.Api -- --init-schema --reset-demo-passwordsSummary
The declarative seed architecture powered by ISeedStep provides robust enterprise guarantees:
- Strict Idempotency: Primary key pre-checks and differential merges guarantee repeated runs never throw constraint errors;
- Zero Production Pollution:
SeedScope.Demois gated strictly by the runner, preventing test data from ever entering production databases; - Topological DAG Safety: The
DependsOndependency graph prevents foreign key violations caused by non-deterministic module loading order.