[teamai] Push 87 resource(s) from XingfenD
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
# 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 |
|
||||
@@ -0,0 +1,49 @@
|
||||
# Testing with samber/do
|
||||
|
||||
## Container Cloning
|
||||
|
||||
Clone containers for isolated tests:
|
||||
|
||||
```go
|
||||
func TestUserService(t *testing.T) {
|
||||
// Create test container by cloning main container
|
||||
testInjector := mainInjector.Clone()
|
||||
|
||||
// Override with mocks
|
||||
mockDB := &MockDatabase{}
|
||||
do.OverrideValue(testInjector, mockDB)
|
||||
|
||||
// Test with mocked dependencies
|
||||
service := do.MustInvoke[UserService](testInjector)
|
||||
// ... test code
|
||||
}
|
||||
```
|
||||
|
||||
## Reusable Test Helpers
|
||||
|
||||
```go
|
||||
func SetupTestContainer(t *testing.T) do.Injector {
|
||||
injector := do.New()
|
||||
|
||||
do.Provide(injector, func(i do.Injector) (Database, error) {
|
||||
return &MockDatabase{}, nil
|
||||
})
|
||||
|
||||
return injector
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Testing & Overrides
|
||||
|
||||
| Function | Purpose |
|
||||
| -------------------------------- | ------------------------------- |
|
||||
| `injector.Clone()` | Clone container for testing |
|
||||
| `injector.CloneWithOpts()` | Clone with custom options |
|
||||
| `do.Override[T]()` | Replace service (use in tests) |
|
||||
| `do.OverrideNamed[T]()` | Replace named service |
|
||||
| `do.OverrideValue[T]()` | Replace value service |
|
||||
| `do.OverrideNamedValue[T]()` | Replace named value |
|
||||
| `do.OverrideTransient[T]()` | Replace transient factory |
|
||||
| `do.OverrideNamedTransient[T]()` | Replace named transient factory |
|
||||
Reference in New Issue
Block a user