Env-selected store adapters — one core, swappable persistence
Env-selected store adapters — one core, swappable persistence
Core services depend on store interfaces, never concrete databases. The wiring layer picks the implementation from env vars, so the same binary runs on memstore (dev/tests), SQLite (standalone/container), or DynamoDB (Lambda/serverless) with zero core changes.
Problem it solves
- Dual deployment needs different persistence per host: in-memory for tests, SQLite for standalone, DynamoDB for serverless. Without adapters, every store call site forks per environment.
Shape
core/store.go-style interface:PutFlow/GetFlow/ListFlows/...,PutIdempotent/GetIdempotent(atomic once-only claim,ErrDuplicate).- Implementations slot in at wiring, not in core:
memstore(dev/tests),dynamostore(Lambda),sqlitestore(standalone). Selection:STORE=mem|dynamo|sqlitestyle env. - Tests use
fakedynamo(in-process DynamoDB double) — no AWS credentials in CI. - Data model split: queues/Dynamo for transaction work; SQL or analytics-direct for long-term stores, chosen per access pattern (not dogmatically).
Rules
- The interface is the contract; adapters must be behaviorally identical (same idempotency, same error semantics).
- Idempotency keys live in the store (
PutIdempotentatomic claim), not in process memory. - Never reach for a concrete store type inside core — only through the interface.
- Document the adapter in the service README (which env var, which table).
Anti-patterns
if env == "prod"branches inside business logic.- Leaking driver-specific types (DynamoDB attribute maps, SQL rows) across the interface.
- Skipping the adapter for "just this one query" — that query becomes the migration blocker.
- Tests that only exercise memstore and never the production adapter's semantics.
Sources
- factory/flow-builder/core/store.go (Store interface + adapter comment)
- factory/paywall (PW_DB=dynamo, PW_DYNAMO_TABLE, awsmini SigV4 no-SDK)
- factory/analytics (dynamostore + fakedynamo tests)
- factory/AGENTS.md (store adapter rule, data model split)