Configuration Options Reference
This guide covers all configuration options available in godi for customizing behavior.
ServiceProviderOptions
Configure behavior when building a service provider:
type ServiceProviderOptions struct {
// Validation
ValidateOnBuild bool
// Callbacks
OnServiceResolved func(serviceType reflect.Type, instance interface{}, duration time.Duration)
OnServiceError func(serviceType reflect.Type, err error)
// Timeouts
ResolutionTimeout time.Duration
// Testing
DryRun bool
// Recovery
RecoverFromPanics bool
// Performance
DeferAcyclicVerification bool
}
ValidateOnBuild
Validates all services can be constructed during BuildServiceProvider:
options := &ServiceProviderOptions{
ValidateOnBuild: true, // Default: false
}
// Catches configuration errors early
provider, err := collection.BuildServiceProviderWithOptions(options)
if err != nil {
// Error indicates misconfiguration
log.Fatal("Service configuration error:", err)
}
When to use:
Production applications
When you want to fail fast
During testing to ensure all dependencies are satisfied
Performance impact:
Increases startup time
Attempts to construct all singleton services
Worth it for catching errors early
OnServiceResolved
Callback after successful service resolution:
options := &ServiceProviderOptions{
OnServiceResolved: func(serviceType reflect.Type, instance interface{}, duration time.Duration) {
// Logging
log.Printf("[DI] Resolved %s in %v", serviceType, duration)
// Metrics
resolveCounter.WithLabelValues(serviceType.String()).Inc()
resolveHistogram.Observe(duration.Seconds())
// Performance monitoring
if duration > 100*time.Millisecond {
log.Warnf("Slow resolution: %s took %v", serviceType, duration)
}
},
}
Use cases:
Performance monitoring
Debugging resolution issues
Metrics collection
Audit logging
OnServiceError
Callback when service resolution fails:
options := &ServiceProviderOptions{
OnServiceError: func(serviceType reflect.Type, err error) {
// Error tracking
errorCounter.WithLabelValues(serviceType.String()).Inc()
// Logging with context
if godi.IsNotFound(err) {
log.Errorf("Service not registered: %s", serviceType)
} else {
log.Errorf("Failed to resolve %s: %v", serviceType, err)
}
// Alert on critical services
if serviceType == reflect.TypeOf((*Database)(nil)).Elem() {
alerting.SendCritical("Database resolution failed", err)
}
},
}
Use cases:
Error monitoring
Debugging missing services
Alerting on critical failures
Error metrics
ResolutionTimeout
Timeout for individual service resolutions:
options := &ServiceProviderOptions{
ResolutionTimeout: 5 * time.Second, // Default: 0 (no timeout)
}
// Resolution will timeout if it takes too long
service, err := godi.Resolve[SlowService](provider)
if godi.IsTimeout(err) {
log.Error("Service resolution timed out")
}
When to use:
Prevent hanging on slow constructors
Detect initialization deadlocks
Enforce SLAs on service creation
Considerations:
Set reasonable timeouts
Account for slow operations (database connections)
May cause false positives under load
DryRun
Disable actual constructor invocation:
options := &ServiceProviderOptions{
DryRun: true, // Default: false
}
// Validates graph without creating instances
provider, err := collection.BuildServiceProviderWithOptions(options)
Use cases:
Validate dependency graph
Testing service configuration
CI/CD validation without side effects
Limitations:
Services are not actually created
Can’t test runtime behavior
Only validates the dependency graph
RecoverFromPanics
Recover from panics in constructors:
options := &ServiceProviderOptions{
RecoverFromPanics: true, // Default: false
}
// Panicking constructor
func NewBadService() *BadService {
panic("initialization failed")
}
// With recovery enabled, returns error instead of panic
service, err := godi.Resolve[BadService](provider)
if err != nil {
log.Error("Service panicked:", err)
}
When to use:
Integration with third-party code
Defensive programming
Development/debugging
Caution:
May hide serious issues
Panics often indicate bugs
Consider fixing the root cause
DeferAcyclicVerification
Defer circular dependency checking:
options := &ServiceProviderOptions{
DeferAcyclicVerification: true, // Default: false
}
// Cycle detection happens on first Invoke instead of Build
provider, err := collection.BuildServiceProviderWithOptions(options)
Benefits:
Faster startup
Lazy validation
Trade-offs:
Errors discovered later
May fail at runtime
Provide Options
Options for service registration:
Name Option
Register keyed/named services:
// Single implementation
collection.AddSingleton(NewRedisCache, godi.Name("redis"))
collection.AddSingleton(NewMemoryCache, godi.Name("memory"))
// Resolve by name
cache, _ := godi.ResolveKeyed[Cache](provider, "redis")
Group Option
Register services in groups:
// Multiple implementations
collection.AddSingleton(NewUserHandler, godi.Group("handlers"))
collection.AddSingleton(NewOrderHandler, godi.Group("handlers"))
collection.AddSingleton(NewProductHandler, godi.Group("handlers"))
// Consume as slice
type App struct {
godi.In
Handlers []Handler `group:"handlers"`
}
As Option
Register service as specific interfaces:
// PostgresDB implements multiple interfaces
collection.AddSingleton(NewPostgresDB,
godi.As(new(Reader), new(Writer), new(Transactor)))
// Can resolve as any of the interfaces
reader, _ := godi.Resolve[Reader](provider)
writer, _ := godi.Resolve[Writer](provider)
Callback Options
Monitor service creation:
var creationTime time.Time
collection.AddSingleton(NewExpensiveService,
godi.WithProviderBeforeCallback(func(info godi.BeforeCallbackInfo) {
creationTime = time.Now()
log.Printf("Creating %s", info.Name)
}),
godi.WithProviderCallback(func(info godi.CallbackInfo) {
duration := time.Since(creationTime)
log.Printf("Created %s in %v", info.Name, duration)
if duration > time.Second {
log.Warn("Slow service creation")
}
}),
)
Decorate Options
Options for service decoration:
// Basic decoration
collection.Decorate(LoggingDecorator)
// With callbacks
collection.Decorate(CachingDecorator,
godi.WithDecoratorCallback(func(info godi.CallbackInfo) {
log.Printf("Decorated service in %v", info.Runtime)
}),
)
Complete Example
func CreateProvider() (godi.ServiceProvider, error) {
collection := godi.NewServiceCollection()
// Register services with options
collection.AddSingleton(NewConfig)
collection.AddSingleton(NewLogger, godi.As(new(Logger)))
// Named services
collection.AddSingleton(NewPostgresDB, godi.Name("primary"))
collection.AddSingleton(NewPostgresReadReplica, godi.Name("replica"))
// Groups
collection.AddSingleton(NewHealthCheck, godi.Group("health"))
collection.AddSingleton(NewReadinessCheck, godi.Group("health"))
// Configure provider
options := &godi.ServiceProviderOptions{
// Validate everything upfront
ValidateOnBuild: true,
// Monitor resolution
OnServiceResolved: func(serviceType reflect.Type, instance interface{}, duration time.Duration) {
metrics.RecordResolution(serviceType, duration)
},
// Track errors
OnServiceError: func(serviceType reflect.Type, err error) {
logger.Error("Resolution failed",
"type", serviceType,
"error", err)
},
// Prevent hanging
ResolutionTimeout: 30 * time.Second,
// Safety
RecoverFromPanics: true,
}
return collection.BuildServiceProviderWithOptions(options)
}
Option Patterns
Development vs Production
func GetProviderOptions(env string) *ServiceProviderOptions {
base := &ServiceProviderOptions{
OnServiceResolved: logResolution,
OnServiceError: logError,
}
switch env {
case "development":
base.ValidateOnBuild = false // Faster startup
base.RecoverFromPanics = true // More forgiving
base.ResolutionTimeout = 0 // No timeout
case "production":
base.ValidateOnBuild = true // Fail fast
base.RecoverFromPanics = false // Don't hide issues
base.ResolutionTimeout = 10 * time.Second
}
return base
}
Testing Configuration
func TestProviderOptions() *ServiceProviderOptions {
return &ServiceProviderOptions{
DryRun: false, // Actually create services
ValidateOnBuild: true, // Catch issues
ResolutionTimeout: 1 * time.Second, // Fail fast in tests
}
}
Monitoring Configuration
func MonitoredProviderOptions(metrics *Metrics) *ServiceProviderOptions {
return &ServiceProviderOptions{
OnServiceResolved: func(t reflect.Type, _ interface{}, d time.Duration) {
metrics.ResolutionDuration.
WithLabelValues(t.String()).
Observe(d.Seconds())
metrics.ResolutionCount.
WithLabelValues(t.String(), "success").
Inc()
},
OnServiceError: func(t reflect.Type, err error) {
metrics.ResolutionCount.
WithLabelValues(t.String(), "error").
Inc()
errorType := "unknown"
if godi.IsNotFound(err) {
errorType = "not_found"
} else if godi.IsCircularDependency(err) {
errorType = "circular"
}
metrics.ResolutionErrors.
WithLabelValues(t.String(), errorType).
Inc()
},
}
}
Best Practices
Always validate in production
options.ValidateOnBuild = true
Set reasonable timeouts
options.ResolutionTimeout = 30 * time.Second
Monitor in production
options.OnServiceResolved = productionMonitoring options.OnServiceError = productionErrorTracking
Don’t hide panics in production
options.RecoverFromPanics = false // Let them crash
Use callbacks for cross-cutting concerns
Logging
Metrics
Tracing
Auditing