[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,92 @@
# google/wire — Compile-Time Code Generation
Wire uses code generation to resolve the dependency graph at compile time. Type-safe, but requires a build step.
- Docs: [github.com/google/wire](https://github.com/google/wire) | [User Guide](https://github.com/google/wire/blob/main/docs/guide.md)
Before writing Wire code, refer to the library's official documentation for up-to-date API signatures and examples.
## Provider Definitions
```go
// providers.go
package wire
import "github.com/google/wire"
// ProviderSet groups related providers
var InfraSet = wire.NewSet(
NewConfig,
NewDatabase,
NewCache,
)
var ServiceSet = wire.NewSet(
NewUserService,
wire.Bind(new(UserStore), new(*PostgresUserStore)), // bind interface to impl
)
```
## Injector Definition
```go
// wire.go — build constraint ensures this is only used by the wire tool
//go:build wireinject
package main
import "github.com/google/wire"
func InitializeApp() (*App, error) {
wire.Build(
InfraSet,
ServiceSet,
NewApp,
)
return nil, nil // wire replaces this body
}
```
## Generated Code
Run `wire ./...` to produce `wire_gen.go`:
```go
// wire_gen.go — DO NOT EDIT (auto-generated by wire)
func InitializeApp() (*App, error) {
config := NewConfig()
database, err := NewDatabase(config)
if err != nil {
return nil, err
}
cache := NewCache(config)
store := NewPostgresUserStore(database)
userService := NewUserService(store, cache)
app := NewApp(userService)
return app, nil
}
```
## Testing
Wire generates plain constructors, so testing uses manual injection — no container to clone:
```go
func TestUserService(t *testing.T) {
mock := &MockUserStore{...}
svc := NewUserService(mock, NewTestCache())
// ... test
}
```
## Tradeoffs
- Errors caught at compile time (codegen fails if graph is incomplete)
- Requires running `wire ./...` after every dependency change
- No lazy loading — all dependencies created eagerly
- No built-in lifecycle management (health checks, shutdown)
- No runtime container — wire generates plain Go constructor calls
- Interface bindings require explicit `wire.Bind` declarations
- Generated files (`wire_gen.go`) must be committed and kept in sync
Wire injectors MUST use `//go:build wireinject` build constraint. Generated `wire_gen.go` MUST NOT be edited manually — always regenerate with `wire ./...`.
@@ -0,0 +1,64 @@
# Manual Constructor Injection
Manual DI is the simplest approach — pass dependencies through constructors. No library, no magic.
## Complete Application Example
```go
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
// Layer 1: Configuration
cfg := LoadConfig()
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
// Layer 2: Infrastructure
db, err := postgres.Connect(cfg.DatabaseURL)
if err != nil {
logger.Error("database connection failed", "error", err)
os.Exit(1)
}
defer db.Close()
cache := redis.NewClient(cfg.RedisURL)
defer cache.Close()
mailer := smtp.NewMailer(cfg.SMTPAddr)
// Layer 3: Repositories
userRepo := postgres.NewUserRepository(db)
orderRepo := postgres.NewOrderRepository(db)
// Layer 4: Services
userSvc := service.NewUserService(userRepo, cache, mailer, logger)
orderSvc := service.NewOrderService(orderRepo, userSvc, logger)
paymentSvc := service.NewPaymentService(orderRepo, cfg.StripeKey, logger)
// Layer 5: Transport
handler := http.NewHandler(userSvc, orderSvc, paymentSvc, logger)
server := http.NewServer(cfg.Port, handler)
// Run
go server.ListenAndServe()
<-ctx.Done()
server.Shutdown(context.Background())
}
```
## When Manual DI Works Well
- Small to medium projects (< 15 services)
- Simple dependency graph with clear layering
- No need for lazy loading or lifecycle management
- Team prefers explicit, visible wiring
## When Manual DI Breaks Down
- Adding a new service means editing `main()` and getting the wiring order right
- Lifecycle management (health checks, graceful shutdown) must be hand-coded with `defer`
- No lazy initialization — all services are created at startup, even if unused
- Cross-cutting concerns (logging, tracing) must be threaded through every constructor
- With 30+ services, the wiring code becomes fragile and hard to maintain
Manual DI SHOULD be the default for small projects (< 15 services). Dependencies MUST be initialized in order — infrastructure first, then repositories, then services, then transport.
@@ -0,0 +1,36 @@
# samber/do — Generics-Based DI
> **For the full samber/do API, patterns, and advanced features, see the `samber/cc-skills-golang@golang-samber-do` skill.**
Type-safe dependency injection using Go generics. No reflection, no code generation, simple API.
- Docs: [do.samber.dev](https://do.samber.dev) | [github.com/samber/do/v2](https://github.com/samber/do)
## Core Pattern
```go
// Register services with providers
injector := do.New()
do.Provide(injector, func(i do.Injector) (*UserService, error) {
db := do.MustInvoke[*Database](i)
return NewUserService(db), nil
})
// Invoke services (lazy — created on demand)
svc := do.MustInvoke[*UserService](injector)
// Graceful shutdown — all services implementing Shutdowner are closed
injector.ShutdownOnSignalsWithContext(ctx, os.Interrupt)
```
## Why samber/do
- **No code generation** — no build step, no generated files to maintain
- **No reflection** — errors are caught at compile time via generics, not at runtime
- **Strongly typed** — Go generics provide full type safety without `interface{}` casts
- **Built-in lifecycle** — health checks and graceful shutdown detected automatically
- **Container cloning** — create isolated test containers from production configuration
- **Simple API** — `Provide`, `Invoke`, `Shutdown` — that's most of what you need
- **Package system** — organize services by domain without manual wiring order
→ See `samber/cc-skills-golang@golang-samber-do` for full application setup, package organization, lifecycle management, debugging, testing with clone + override, and complete API reference.
@@ -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.