[teamai] Push 87 resource(s) from XingfenD
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
# uber-go/dig + uber-go/fx — Reflection-Based DI
|
||||
|
||||
`dig` is the low-level DI container; `fx` is the full application framework built on top. Powerful but uses reflection — errors appear at startup, not compile time.
|
||||
|
||||
- Docs: [github.com/uber-go/dig](https://github.com/uber-go/dig) | [uber-go.github.io/fx](https://uber-go.github.io/fx/)
|
||||
|
||||
Before writing dig/fx code, refer to the library's official documentation for up-to-date API signatures and examples.
|
||||
|
||||
## dig — Basic Container
|
||||
|
||||
```go
|
||||
func main() {
|
||||
container := dig.New()
|
||||
|
||||
container.Provide(NewConfig)
|
||||
container.Provide(NewDatabase)
|
||||
container.Provide(NewUserStore)
|
||||
container.Provide(NewUserService)
|
||||
|
||||
// Invoke — dig resolves the full dependency chain
|
||||
err := container.Invoke(func(svc *UserService) {
|
||||
svc.Run()
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Named Dependencies
|
||||
|
||||
```go
|
||||
type DatabaseParams struct {
|
||||
dig.In
|
||||
|
||||
Primary *sql.DB `name:"primary"`
|
||||
Replica *sql.DB `name:"replica"`
|
||||
}
|
||||
|
||||
container.Provide(NewPrimaryDB, dig.Name("primary"))
|
||||
container.Provide(NewReplicaDB, dig.Name("replica"))
|
||||
|
||||
container.Provide(func(p DatabaseParams) *UserService {
|
||||
return &UserService{
|
||||
writer: p.Primary,
|
||||
reader: p.Replica,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### dig Tradeoffs
|
||||
|
||||
- Uses reflection — type mismatches are runtime errors, not compile errors
|
||||
- `dig.In` and `dig.Out` structs add boilerplate for complex graphs
|
||||
- No built-in lifecycle management
|
||||
- Powerful grouping with `dig.Group` for collecting multiple implementations
|
||||
|
||||
## fx — Full Application Framework
|
||||
|
||||
### Basic Application
|
||||
|
||||
```go
|
||||
func main() {
|
||||
app := fx.New(
|
||||
fx.Provide(
|
||||
NewConfig,
|
||||
NewDatabase,
|
||||
NewUserStore,
|
||||
NewUserService,
|
||||
),
|
||||
fx.Invoke(RegisterRoutes),
|
||||
fx.Invoke(StartServer),
|
||||
)
|
||||
|
||||
app.Run() // blocks until signal, then calls shutdown hooks
|
||||
}
|
||||
```
|
||||
|
||||
### Lifecycle Hooks
|
||||
|
||||
```go
|
||||
func NewDatabase(lc fx.Lifecycle, cfg *Config) (*Database, error) {
|
||||
db := &Database{}
|
||||
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(ctx context.Context) error {
|
||||
return db.Connect(cfg.URL)
|
||||
},
|
||||
OnStop: func(ctx context.Context) error {
|
||||
return db.Close()
|
||||
},
|
||||
})
|
||||
|
||||
return db, nil
|
||||
}
|
||||
```
|
||||
|
||||
### Modules
|
||||
|
||||
```go
|
||||
var InfraModule = fx.Module("infra",
|
||||
fx.Provide(NewConfig),
|
||||
fx.Provide(NewDatabase),
|
||||
fx.Provide(NewCache),
|
||||
)
|
||||
|
||||
var ServiceModule = fx.Module("service",
|
||||
fx.Provide(NewUserService),
|
||||
fx.Provide(NewOrderService),
|
||||
)
|
||||
|
||||
app := fx.New(InfraModule, ServiceModule, fx.Invoke(StartServer))
|
||||
```
|
||||
|
||||
### Testing with fx
|
||||
|
||||
```go
|
||||
func TestUserService(t *testing.T) {
|
||||
var svc *UserService
|
||||
|
||||
app := fxtest.New(t,
|
||||
fx.Provide(NewMockUserStore),
|
||||
fx.Provide(NewUserService),
|
||||
fx.Populate(&svc),
|
||||
)
|
||||
app.RequireStart()
|
||||
defer app.RequireStop()
|
||||
|
||||
// ... test svc
|
||||
}
|
||||
```
|
||||
|
||||
### fx Tradeoffs
|
||||
|
||||
- Full application framework — manages startup, shutdown, and signal handling
|
||||
- Reflection-based — errors at startup, not compile time
|
||||
- Steep learning curve — `fx.In`, `fx.Out`, `fx.Annotate`, `fx.Decorate`
|
||||
- Built-in lifecycle (OnStart/OnStop hooks)
|
||||
- Heavyweight — pulls in the full fx framework
|
||||
- `fxtest` package for testing, but requires starting/stopping the app
|
||||
|
||||
fx lifecycle hooks MUST be used for start/stop — register `OnStart`/`OnStop` via `fx.Lifecycle`. fx modules SHOULD group related providers — use `fx.Module` to organize by domain.
|
||||
Reference in New Issue
Block a user