All Relay persistence backends are built on Grove -- the ORM and KV abstraction layer used across the Forge ecosystem. This means every store follows the same patterns for connection management, migrations, and DI integration.
Architecture01
Relay's stores fall into two categories based on the Grove layer they use:
| Layer | Stores | Description |
Grove ORM (grove.DB) | PostgreSQL, SQLite, MongoDB | Struct-based models with query builders and the Grove migrator |
Grove KV (kv.Store) | Redis | Key-value storage with JSON-serialized entities and sorted set indexes |
Constructor pattern02
Every store accepts a Grove connection and returns a store.Store:
// SQL stores accept *grove.DB
pgStore := pgstore.New(db)
sqlStore := sqlitestore.New(db)
mongoStore := mongostore.New(db)
// KV stores accept *kv.Store
redisStore := redisstore.New(kvStore)Auto-detection with Forge03
When using the Forge extension, you don't need to import individual store packages. The extension resolves a grove.DB or kv.Store from the DI container and auto-constructs the correct store based on the driver type:
// Detects pg/sqlite/mongo driver and builds the right store
app.Use(extension.New(
extension.WithGroveDatabase(""),
))
// Or for Redis via KV
app.Use(extension.New(
extension.WithGroveKV(""),
))The driver detection maps:
| Grove driver | Store package |
pg | store/postgres |
sqlite | store/sqlite |
mongo | store/mongo |
| Redis (via KV) | store/redis |
Named databases04
In multi-database Forge apps, reference a specific Grove database by name:
extension.WithGroveDatabase("webhooks") // named DB
extension.WithGroveKV("relay-cache") // named KVOr via YAML:
extensions:
relay:
grove_database: webhooks
grove_kv: relay-cacheStore resolution order05
The extension resolves its store in this order:
Explicit store -- if
relay.WithStore(s)was passed as an option, it is used directly.Grove database -- if
WithGroveDatabasewas called (orgrove_databaseis set in YAML), the named or defaultgrove.DBis resolved from DI and the driver type determines the store.Grove KV -- if
WithGroveKVwas called (orgrove_kvis set in YAML), the named or defaultkv.Storeis resolved from DI.Fallback -- if none of the above is configured, no store is set and
relay.New()returnsErrNoStoreunless a store was provided via other means.
Migrations06
All Grove ORM stores use the Grove migrator (grove/migrate). Each store registers a migration group named "relay":
var Migrations = migrate.NewGroup("relay")The Grove migrator supports:
Version tracking in a
_migrationstableUp/down migration functions
Orchestrated execution with locking
The Redis store does not require migrations (keys are created on demand).