207 lines
5.0 KiB
Markdown
207 lines
5.0 KiB
Markdown
# Advanced Usage
|
|
|
|
## Scopes (Module Tree)
|
|
|
|
Scopes SHOULD be used to organize services by module:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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):
|
|
|
|
```go
|
|
// 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:
|
|
|
|
```go
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
report := injector.ShutdownWithContext(ctx)
|
|
```
|
|
|
|
## Debugging
|
|
|
|
### List Services
|
|
|
|
```go
|
|
services := injector.ListProvidedServices()
|
|
for _, svc := range services {
|
|
fmt.Printf("%s: %s\n", svc.ScopeName, svc.Service)
|
|
}
|
|
```
|
|
|
|
### Explain Injector
|
|
|
|
```go
|
|
explanation := do.ExplainInjector(injector)
|
|
fmt.Println(explanation.String())
|
|
```
|
|
|
|
## Migration from Manual DI
|
|
|
|
Before (manual):
|
|
|
|
```go
|
|
func main() {
|
|
config := &Config{Port: 8080}
|
|
db := NewDatabase(config)
|
|
userRepo := NewUserRepository(db)
|
|
userService := NewUserService(userRepo)
|
|
api := NewAPI(userService)
|
|
}
|
|
```
|
|
|
|
After (with do):
|
|
|
|
```go
|
|
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 |
|