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
SeedingContextrecord replaces the loosestring environmentparameter onIReferenceDataSeeder.SeedAsync(). It carriesEnvironment(string) andSeedDevelopmentData(bool).DYNAPLEX_SEED_DEVELOPMENT_DATAenvironment variable controls demo data independently. Default:truewhenIHostEnvironment.IsDevelopment(),falseotherwise. Explicittrue/falseoverrides the default.WithDevelopmentData()extension on the orchestration builder sets the env var on db-manager, making it declarative in AppHost configuration.SeederUtilitiesin Strata Core extracts shared helper methods (EscapeCurlyBraces, path resolvers) previously copy-pasted across seeders.All engines with demo data follow the same pattern: check
context.SeedDevelopmentDatainstead 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 EXISTSchecks by name+tenant, andON CONFLICT DO NOTHINGon passport inserts - Well-known UUIDs: Use ranges in
00000000-0000-0000-0000-0000000000XXformat, 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.SeedDevelopmentDatainstead 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
IReferenceDataSeedersignature changed fromSeedAsync(string, ...)toSeedAsync(SeedingContext, ...)SeederUtilitiesadded toAcsis.Dynaplex.Strata.Core— available to all Database projectsMigrationWorkerconstructsSeedingContextonce per seeder invocation- Project template (
cortex/project-templates) may need updating if it references the old signature WithDevelopmentData()available onIDistributedApplicationBuilderafterAddDynaplexDbManager()
Alternatives Considered
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.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.
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.
Related
strata/core/src/.../IReferenceDataSeeder.cs— Interface definitionstrata/core/src/.../SeedingContext.cs— Configuration recordstrata/core/src/.../SeederUtilities.cs— Shared utilitiesengines/db-manager/src/.../MigrationWorker.cs— Context constructionstrata/orchestration/src/.../DynaplexInfrastructureExtensions.cs—WithDevelopmentData()- ADR-036: Database project separation (seeder directory structure)
- ADR-037: Explicit tenant scope operations (seeder system scope pattern)