Documentation

adrs/061-development-data-seeding-protocol.md


title: "ADR-061: Development Data Seeding Protocol"
authors:

  • Daneel

ADR-061: Development Data Seeding Protocol

Status: Accepted

Context

The Dynaplex seeding infrastructure supports two tiers of data: reference data (always seeded) and development/demo data (seeded conditionally). Prior to this decision, the conditional logic was:

  • Hardcoded to string.Equals(environment, "Development", ...) in each seeder
  • Not configurable independently of the .NET environment name
  • Inconsistent across engines — some had demo data, others did not
  • Duplicated utility code (EscapeCurlyBraces) across 5 seeders

This made it impossible to seed demo data in CI/test environments without running as "Development", and left Playwright E2E tests with no data to exercise.

Decision

Formalize the two-tier seeding protocol with a structured SeedingContext and explicit configuration.

Core Changes

  1. SeedingContext record replaces the loose string environment parameter on IReferenceDataSeeder.SeedAsync(). It carries Environment (string) and SeedDevelopmentData (bool).

  2. DYNAPLEX_SEED_DEVELOPMENT_DATA environment variable controls demo data independently. Default: true when IHostEnvironment.IsDevelopment(), false otherwise. Explicit true/false overrides the default.

  3. WithDevelopmentData() extension on the orchestration builder sets the env var on db-manager, making it declarative in AppHost configuration.

  4. SeederUtilities in Strata Core extracts shared helper methods (EscapeCurlyBraces, path resolvers) previously copy-pasted across seeders.

  5. All engines with demo data follow the same pattern: check context.SeedDevelopmentData instead of comparing environment strings.

Two-Tier Data Protocol

Tier When Examples Convention
Reference Always Platform types, countries, identifier types, built-in roles Seeding/ReferenceData/*.sql
Development When SeedDevelopmentData == true Demo items, demo locations, demo workflows, dev users Seeding/DemoData/*.sql

SQL Conventions

  • Idempotent: Use IF NOT EXISTS checks by name+tenant, and ON CONFLICT DO NOTHING on passport inserts
  • Well-known UUIDs: Use ranges in 00000000-0000-0000-0000-0000000000XX format, documented per engine
  • Passport integration: Every entity with a Passport ID inserts its passport row first
  • Resolved IDs: Query actual IDs after conditional inserts to handle user-created entities gracefully

UUID Ranges by Engine

Engine Range Purpose
Catalog (ref) ...0100 - ...0114 RFID categories, types, statuses
Spatial (demo) ...0200 - ...0216 Demo locations, categories
Catalog (demo) ...0300 - ...041C Demo categories, types, items
Transport (demo) ...0500 - ...05XX Demo shipments
Workflow (demo) ...0600 - ...061B Demo workflows, statuses

Consequences

Positive:

  • Playwright tests can seed demo data in CI by setting DYNAPLEX_SEED_DEVELOPMENT_DATA=true
  • Seeders are decoupled from the .NET environment name
  • Consistent pattern across all engines
  • Reduced code duplication via SeederUtilities

Negative:

  • Breaking interface change on IReferenceDataSeeder (all implementors updated in one commit)
  • Slight indirection: seeders check context.SeedDevelopmentData instead of a simple string

Neutral:

  • Default behavior is unchanged — Development environments still seed demo data automatically
  • Engines without demo data (CoreData, Prism, BBU) are unaffected; they accept the context but don't use SeedDevelopmentData

Dynaplex Impact

  • IReferenceDataSeeder signature changed from SeedAsync(string, ...) to SeedAsync(SeedingContext, ...)
  • SeederUtilities added to Acsis.Dynaplex.Strata.Core — available to all Database projects
  • MigrationWorker constructs SeedingContext once per seeder invocation
  • Project template (cortex/project-templates) may need updating if it references the old signature
  • WithDevelopmentData() available on IDistributedApplicationBuilder after AddDynaplexDbManager()

Alternatives Considered

  1. Environment variable only (no SeedingContext): Simpler, but every seeder would need to read configuration independently. The structured context is more extensible and centralizes the logic in MigrationWorker.

  2. Separate seeder interface for demo data: Would avoid changing the existing interface but doubles the registration and invocation complexity. The two-tier pattern within a single seeder is already well-established.

  3. Feature flags via app configuration: Overkill for a binary on/off toggle. The env var approach is standard for container orchestration and CI pipelines.

Reversibility

Low risk. Reverting requires changing the interface signature back to string environment and updating all 9 seeders — mechanical but straightforward. The SeedingContext record and SeederUtilities can be removed without affecting other systems.

  • strata/core/src/.../IReferenceDataSeeder.cs — Interface definition
  • strata/core/src/.../SeedingContext.cs — Configuration record
  • strata/core/src/.../SeederUtilities.cs — Shared utilities
  • engines/db-manager/src/.../MigrationWorker.cs — Context construction
  • strata/orchestration/src/.../DynaplexInfrastructureExtensions.csWithDevelopmentData()
  • ADR-036: Database project separation (seeder directory structure)
  • ADR-037: Explicit tenant scope operations (seeder system scope pattern)