Forge Integration
How Forge exposes Vessel for app services and extension wiring
Forge uses Vessel as its internal dependency injection engine. Every Forge application creates a Vessel container at startup and exposes it via app.Container(). Forge also re-exports Vessel functions under the forge package for convenience, along with backward-compatible named-registration helpers.
Accessing the Container
import "github.com/xraph/forge"
app := forge.New(
forge.WithAppName("my-app"),
forge.WithAppVersion("1.0.0"),
)
// The container is a vessel.Vessel instance
c := app.Container()forge.Container is a type alias for vessel.Vessel, so all Vessel functions work directly with the value returned by app.Container().
Core Services Registered by Forge
When a Forge app is created, these services are automatically registered in the container -- both by name (for backward compatibility) and by type (for constructor injection):
| Service | Type | Name Key |
|---|---|---|
| Logger | forge.Logger | "logger" |
| Config Manager | forge.ConfigManager | "forge.config" |
| Metrics | forge.Metrics | "metrics" |
| Health Manager | forge.HealthManager | "forge.health" |
| Router | forge.Router | "forge.router" |
Resolving Core Services
By type (recommended):
logger, err := forge.Inject[forge.Logger](app.Container())
metrics, err := forge.Inject[forge.Metrics](app.Container())By name:
logger, err := forge.InjectNamed[forge.Logger](app.Container(), "logger")Forge also provides convenience helpers:
logger, err := forge.GetLogger(app.Container())
metrics, err := forge.GetMetrics(app.Container())
healthMgr, err := forge.GetHealthManager(app.Container())Forge Re-exports
Forge re-exports all Vessel DI functions under the forge package. You can use either vessel.* or forge.* -- they call the same underlying functions.
Constructor Registration and Resolution
| Forge function | Vessel equivalent |
|---|---|
forge.Provide(c, constructor, opts...) | vessel.Provide(c, constructor, opts...) |
forge.ProvideConstructor(c, constructor, opts...) | vessel.Provide(c, constructor, opts...) |
forge.ProvideValue[T](c, value, opts...) | vessel.ProvideValue[T](c, value, opts...) |
forge.Inject[T](c) | vessel.Inject[T](c) |
forge.MustInject[T](c) | vessel.MustInject[T](c) |
forge.InjectNamed[T](c, name) | vessel.InjectNamed[T](c, name) |
forge.MustInjectNamed[T](c, name) | vessel.MustInjectNamed[T](c, name) |
forge.InjectGroup[T](c, group) | vessel.InjectGroup[T](c, group) |
forge.MustInjectGroup[T](c, group) | vessel.MustInjectGroup[T](c, group) |
forge.HasType[T](c) | vessel.HasType[T](c) |
forge.HasTypeNamed[T](c, name) | vessel.HasTypeNamed[T](c, name) |
Named Registration Helpers (Forge-only)
These are backward-compatible helpers that Forge provides on top of Vessel. They accept a func(Container) (T, error) factory and translate it into the appropriate vessel.ProvideNamed call.
| Forge function | What it does |
|---|---|
forge.RegisterSingleton[T](c, name, factory) | vessel.ProvideNamed + AsSingleton() |
forge.RegisterTransient[T](c, name, factory) | vessel.ProvideNamed + AsTransient() |
forge.RegisterScoped[T](c, name, factory) | vessel.ProvideNamed + AsScoped() |
forge.RegisterValue[T](c, name, value) | vessel.ProvideValue + WithName(name) |
forge.Resolve[T](c, name) | vessel.InjectNamed[T](c, name) |
forge.Must[T](c, name) | vessel.MustInjectNamed[T](c, name) |
Wrapper Type Aliases
| Forge alias | Vessel type |
|---|---|
forge.LazyRef[T] | vessel.Lazy[T] |
forge.OptionalLazyRef[T] | vessel.OptionalLazy[T] |
forge.ProviderRef[T] | vessel.Provider[T] |
Factory helpers:
forge.NewLazyRef[T](c, name) // vessel.NewLazy[T](c, name)
forge.NewOptionalLazyRef[T](c, name) // vessel.NewOptionalLazy[T](c, name)
forge.NewProviderRef[T](c, name) // vessel.NewProvider[T](c, name)Writing Extensions
Extensions register their services during the Register phase using the container from app.Container().
import (
"context"
"github.com/xraph/forge"
"github.com/xraph/vessel"
)
type MyExtension struct {
app forge.App
}
func (e *MyExtension) Register(app forge.App) error {
e.app = app
c := app.Container()
// Register services using vessel (or forge -- equivalent)
vessel.Provide(c, NewMyService)
return nil
}
func (e *MyExtension) Start(ctx context.Context) error {
svc, err := vessel.Inject[*MyService](e.app.Container())
if err != nil {
return err
}
return svc.Start(ctx)
}
func (e *MyExtension) Stop(ctx context.Context) error {
return nil
}Depending on Other Extensions
Use constructor injection to depend on services from other extensions. Because constructors are resolved by type, there is no need to coordinate string keys across extensions.
// This extension depends on the database extension's DatabaseManager
func NewMyService(manager *database.DatabaseManager, logger forge.Logger) (*MyService, error) {
db, err := manager.SQL("primary")
if err != nil {
return nil, err
}
return &MyService{db: db, logger: logger}, nil
}
func (e *MyExtension) Register(app forge.App) error {
return vessel.Provide(app.Container(), NewMyService)
}Resolving Services During Registration
If you need a service to be fully started before using it during the Register phase, use the container's ResolveReady method:
func (e *MyExtension) Register(app forge.App) error {
ctx := context.Background()
svc, err := app.Container().ResolveReady(ctx, "database-manager")
if err != nil {
return fmt.Errorf("database required: %w", err)
}
dbManager := svc.(*database.DatabaseManager)
e.redis, _ = dbManager.Redis("cache")
return nil
}Interface Resolution Across Extensions
Use interface types for loose coupling between extensions:
// Extension A registers a concrete implementation as an interface
vessel.Provide(app.Container(), NewEventService, vessel.As(new(core.EventBus)))
// Extension B resolves by interface type
eventBus, err := vessel.Inject[core.EventBus](app.Container())Which Package to Import
| Scenario | Recommendation |
|---|---|
| Standalone library (no Forge) | import "github.com/xraph/vessel" |
| Forge application code | Either works; forge is convenient for type aliases |
| Forge extension | vessel for registration, forge for types like forge.Logger |
| Shared package used in both | import "github.com/xraph/vessel" |
How is this guide?