Files

5.0 KiB

Advanced Usage

Scopes (Module Tree)

Scopes SHOULD be used to organize services by module:

root := do.New()

// Register shared services in root
do.Provide(root, func(i do.Injector) (Database, error) {
    return &Database{}, nil
})

// Create child scope
apiScope := root.Scope("api")

// Services in apiScope can access root services
do.Provide(apiScope, func(i do.Injector) (UserService, error) {
    db := do.MustInvoke[Database](i) // from root
    return &userService{db: db}, nil
})

// Child scopes are isolated from each other
userScope := root.Scope("user")

Organize services by lifecycle and visibility:

root := do.New()

// Global/stateless services in root
do.Provide(root, NewConfig)
do.Provide(root, NewLogger)

// Request-scoped services
requestScope := root.Scope("request")
do.Provide(requestScope, NewRequestContext)

Explicit Service Aliasing

For rare cases when you need to adapt to legacy code:

do.Provide(injector, func(i do.Injector) (*PostgreSQLDatabase, error) {
    return &PostgreSQLDatabase{}, nil
})

do.MustAs[*PostgreSQLDatabase, Database](injector)

// Now both work:
db1 := do.MustInvoke[*PostgreSQLDatabase](injector)
db2 := do.MustInvoke[Database](injector)

Prefer implicit aliasing with InvokeAs() in most cases.

Struct Injection

Inject services directly into struct fields using tags:

type App struct {
    Database *Database `do:""`
    Logger   *Logger   `do:"app-logger"`
    Config   *Config   `do:""`
}

app := do.MustInvokeStruct[App](injector)

Lifecycle Management

Health Checks

Implement the Healthchecker interface:

func (d *Database) HealthCheck() error {
    return d.conn.Ping()
}

// With context support:
func (d *Database) HealthCheck(ctx context.Context) error {
    return d.conn.PingContext(ctx)
}

// Check health
if err := do.HealthCheck[Database](injector); err != nil {
    log.Printf("Database unhealthy: %v", err)
}

Graceful Shutdown

Implement the Shutdowner interface (4 variants):

// Simple
func (d *Database) Shutdown() { d.conn.Close() }

// With context
func (d *Database) Shutdown(ctx context.Context) { d.conn.Close() }

// With error
func (d *Database) Shutdown() error { return d.conn.Close() }

// With context + error (most flexible)
func (d *Database) Shutdown(ctx context.Context) error { return d.conn.Close() }

Shutdown with timeout:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

report := injector.ShutdownWithContext(ctx)

Debugging

List Services

services := injector.ListProvidedServices()
for _, svc := range services {
    fmt.Printf("%s: %s\n", svc.ScopeName, svc.Service)
}

Explain Injector

explanation := do.ExplainInjector(injector)
fmt.Println(explanation.String())

Migration from Manual DI

Before (manual):

func main() {
    config := &Config{Port: 8080}
    db := NewDatabase(config)
    userRepo := NewUserRepository(db)
    userService := NewUserService(userRepo)
    api := NewAPI(userService)
}

After (with do):

func main() {
    injector := do.New()
    do.Provide(injector, func(i do.Injector) (*Config, error) {
        return &Config{Port: 8080}, nil
    })
    do.Provide(injector, NewDatabase)
    // ... register other services
    api := do.MustInvoke[*API](injector)
}

Quick Reference

Aliasing

Function Purpose
do.As[Initial, Alias]() Create type alias
do.AsNamed[Initial, Alias]() Create named type alias

Lifecycle & Health

Function Purpose
do.HealthCheck[T]() Check service health
do.HealthCheckNamed() Check named service health
do.HealthCheckWithContext[T]() Health check with timeout
do.Shutdown[T]() Gracefully shutdown service
do.ShutdownNamed() Shutdown named service
do.ShutdownWithContext[T]() Shutdown with timeout
do.MustShutdown[T]() Shutdown (panic on error)

Container Management

Function Purpose
do.New() Create new root container
do.NewWithOpts() Create container with options
injector.Scope() Create child scope

Debugging

Function Purpose
do.ExplainInjector() Visualize scope tree and services
do.ExplainService[T]() Get service details and dependencies
do.NameOf[T]() Get service name (use sparingly)
injector.ListProvidedServices() List all available services
injector.ListInvokedServices() List invoked services only