Use Grove instead.
Preferred Pattern01
Use constructor registration and type-based injection as the default style.
// Register constructors
forge.ProvideConstructor(c, func(db *Database, logger forge.Logger) *UserService {
return &UserService{db: db, logger: logger}
})
// Resolve by type (no string keys needed)
userService, err := forge.InjectType[*UserService](c)
eventBus, _ := vessel.InjectType[interfaces.EventBus](app.Container())Extension Registration Graph02
database.Extension.Register(app) performs:
configuration load and validation
eager registration of
*DatabaseManagerregistration of default
Databaseaccessorregistration of type-specific accessor for default DB type:
SQL default ->
*bun.DBMongo default ->
*mongo.ClientRedis default ->
redis.UniversalClient
Key Service Identifiers03
Constants in package:
database.ManagerKeydatabase.DatabaseKeydatabase.SQLKeydatabase.MongoKeydatabase.RedisKey
Primary resolution path in modern code should still be type-based helpers.
Runtime Lifecycle04
DatabaseManager.Start(ctx):marks manager started
opens all registered databases (idempotent)
DatabaseManager.Stop(ctx):marks manager stopped
closes all databases
DatabaseManager.Health(ctx):fails if any registered database is unhealthy
Extension-level lifecycle methods only mark extension started/stopped; manager owns connection lifecycle.
Recommended Access Patterns05
By Type + Name (Best for multi-db)
manager, _ := database.GetManager(app.Container())
primary, _ := manager.SQL("primary")
analytics, _ := manager.Mongo("analytics")
cache, _ := manager.Redis("cache")Through Helpers
primary, _ := database.GetSQL(app.Container(), "primary")
cache, _ := database.GetRedis(app.Container(), "cache")App-Level Convenience
primary, _ := database.GetSQLFromApp(app, "primary")When Registering Your Own Services06
Inject *database.DatabaseManager and resolve named DBs inside constructors. This keeps your service constructors explicit and avoids implicit assumptions about default DB type.