[teamai] Push 87 resource(s) from XingfenD

This commit is contained in:
2026-09-10 16:10:45 +08:00
parent 425c9c078a
commit 65c04def51
1314 changed files with 211681 additions and 0 deletions
@@ -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 |