golang-samber-do

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Persona: You are a Go architect setting up dependency injection. You keep the container at the composition root, depend on interfaces not concrete types, and treat provider errors as first-class failures.
角色定位: 你是一名负责搭建依赖注入的Go架构师。你将容器置于组合根,依赖接口而非具体类型,并将提供者错误视为首要故障。

Using samber/do for Dependency Injection in Go

使用samber/do实现Go语言依赖注入

Type-safe dependency injection toolkit for Go based on Go 1.18+ generics.
Official Resources:
This skill is not exhaustive. Please refer to library documentation and code examples for more informations. Context7 can help as a discoverability platform.
DO NOT USE v1 OF THIS LIBRARY. INSTALL v2 INSTEAD:
bash
go get -u github.com/samber/do/v2
基于Go 1.18+泛型的类型安全Go语言依赖注入工具包。
官方资源:
本技能内容并非详尽无遗。如需更多信息,请参考库文档和代码示例。Context7可作为一个发现平台提供帮助。
请勿使用该库的v1版本,请安装v2版本:
bash
go get -u github.com/samber/do/v2

Core Concepts

核心概念

The Injector (Container)

注入器(容器)

go
import "github.com/samber/do/v2"

injector := do.New()
go
import "github.com/samber/do/v2"

injector := do.New()

Service Types

服务类型

  • Lazy (default): Created when first requested
  • Eager: Created immediately when the container starts
  • Transient: New instance created on every request
  • Value: Pre-created value, no instantiation
  • 延迟加载(默认):首次请求时创建实例
  • 立即加载:容器启动时立即创建实例
  • 瞬时型:每次请求都创建新实例
  • 值类型:预先创建的值,无需实例化

Provider Functions

提供者函数

Services MUST be registered via provider functions:
go
type Provider[T any] func(i Injector) (T, error)
服务必须通过提供者函数注册:
go
type Provider[T any] func(i Injector) (T, error)

Basic Usage

基础用法

1. Define and Register Services

1. 定义并注册服务

Follow "Accept Interfaces, Return Structs":
go
// Register a service (lazy by default)
do.Provide(injector, func(i do.Injector) (Database, error) {
    return &PostgreSQLDatabase{connString: "postgres://..."}, nil
})

// Register a pre-created value
do.ProvideValue(injector, &Config{Port: 8080})

// Register a transient service (new instance each time)
do.ProvideTransient(injector, func(i do.Injector) (*Logger, error) {
    return &Logger{}, nil
})

// Register an eager service (created immediately)
do.Provide(injector, do.Eager(&Config{Port: 8080}))
遵循“接受接口,返回结构体”原则:
go
// 注册服务(默认延迟加载)
do.Provide(injector, func(i do.Injector) (Database, error) {
    return &PostgreSQLDatabase{connString: "postgres://..."}, nil
})

// 注册预先创建的值
do.ProvideValue(injector, &Config{Port: 8080})

// 注册瞬时型服务(每次请求创建新实例)
do.ProvideTransient(injector, func(i do.Injector) (*Logger, error) {
    return &Logger{}, nil
})

// 注册立即加载服务(容器启动时立即创建)
do.Provide(injector, do.Eager(&Config{Port: 8080}))

2. Invoke Services

2. 调用服务

The container MUST only be accessed at the composition root:
go
// Invoke with error handling
db, err := do.Invoke[Database](injector)

// MustInvoke panics on error (use when confident service exists)
db := do.MustInvoke[Database](injector)
容器只能在组合根中访问:
go
// 带错误处理的调用
db, err := do.Invoke[Database](injector)

// MustInvoke在出错时会panic(当你确定服务存在时使用)
db := do.MustInvoke[Database](injector)

3. Service Dependencies

3. 服务依赖

go
func NewUserService(i do.Injector) (UserService, error) {
    db := do.MustInvoke[Database](i)
    cache := do.MustInvoke[Cache](i)
    return &userService{db: db, cache: cache}, nil
}

do.Provide(injector, NewUserService)
go
func NewUserService(i do.Injector) (UserService, error) {
    db := do.MustInvoke[Database](i)
    cache := do.MustInvoke[Cache](i)
    return &userService{db: db, cache: cache}, nil
}

do.Provide(injector, NewUserService)

4. Implicit Aliasing (Preferred)

4. 隐式别名(推荐)

Register a concrete type and invoke as an interface without explicit aliasing:
go
// Register concrete type
do.Provide(injector, func(i do.Injector) (*PostgreSQLDatabase, error) {
    return &PostgreSQLDatabase{}, nil
})

// Invoke directly as interface (implicit aliasing)
db := do.MustInvokeAs[Database](injector)
注册具体类型并直接以接口类型调用,无需显式别名:
go
// 注册具体类型
do.Provide(injector, func(i do.Injector) (*PostgreSQLDatabase, error) {
    return &PostgreSQLDatabase{}, nil
})

// 直接以接口类型调用(隐式别名)
db := do.MustInvokeAs[Database](injector)

5. Named Services

5. 命名服务

Register multiple services of the same type:
go
do.ProvideNamed(injector, "primary-db", func(i do.Injector) (*Database, error) {
    return &Database{URL: "postgres://primary..."}, nil
})

mainDB := do.MustInvokeNamed[*Database](injector, "primary-db")
注册多个同类型服务:
go
do.ProvideNamed(injector, "primary-db", func(i do.Injector) (*Database, error) {
    return &Database{URL: "postgres://primary..."}, nil
})

mainDB := do.MustInvokeNamed[*Database](injector, "primary-db")

Package Organization

包组织

Use
do.Package()
to organize service registration by module:
go
// infrastructure/package.go
var Package = do.Package(
    do.Lazy(func(i do.Injector) (*postgres.DB, error) {
        cfg := do.MustInvoke[*Config](i)
        return postgres.Connect(cfg.DatabaseURL)
    }),
    do.Lazy(func(i do.Injector) (*redis.Client, error) {
        cfg := do.MustInvoke[*Config](i)
        return redis.NewClient(cfg.RedisURL), nil
    }),
)

// main.go
injector := do.New(infrastructure.Package, service.Package)
使用
do.Package()
按模块组织服务注册:
go
// infrastructure/package.go
var Package = do.Package(
    do.Lazy(func(i do.Injector) (*postgres.DB, error) {
        cfg := do.MustInvoke[*Config](i)
        return postgres.Connect(cfg.DatabaseURL)
    }),
    do.Lazy(func(i do.Injector) (*redis.Client, error) {
        cfg := do.MustInvoke[*Config](i)
        return redis.NewClient(cfg.RedisURL), nil
    }),
)

// main.go
injector := do.New(infrastructure.Package, service.Package)

Full Application Setup

完整应用搭建

go
func main() {
    injector := do.New(
        infrastructure.Package,
        repository.Package,
        service.Package,
        transport.Package,
    )

    server := do.MustInvoke[*http.Server](injector)
    go server.ListenAndServe()

    _ = injector.ShutdownOnSignalsWithContext(context.Background(), os.Interrupt)
}
go
func main() {
    injector := do.New(
        infrastructure.Package,
        repository.Package,
        service.Package,
        transport.Package,
    )

    server := do.MustInvoke[*http.Server](injector)
    go server.ListenAndServe()

    _ = injector.ShutdownOnSignalsWithContext(context.Background(), os.Interrupt)
}

Best Practices

最佳实践

  1. Depend on interfaces, not concrete types — lets you swap implementations in tests without touching production code
  2. Each service should have one job — services with multiple responsibilities are harder to test and harder to replace
  3. Keep dependency trees shallow — chains beyond 3-4 levels make initialization order fragile and errors harder to trace
  4. Handle errors in provider functions — a silently failing provider creates a broken service that crashes later in unexpected places
  5. Use scopes to organize services by lifecycle — request-scoped services prevent leaks, global services prevent redundant initialization
For scopes, lifecycle management, struct injection, and debugging, see Advanced Usage.
For testing patterns (cloning, overrides, mocks), see Testing.
  1. 依赖接口而非具体类型——这样你可以在测试中替换实现,无需修改生产代码
  2. 每个服务应只负责一项工作——承担多个职责的服务更难测试和替换
  3. 保持依赖树较浅——超过3-4层的依赖链会让初始化顺序变得脆弱,且错误更难追踪
  4. 在提供者函数中处理错误——静默失败的提供者会创建一个有问题的服务,在后续意想不到的地方崩溃
  5. 使用作用域按生命周期组织服务——请求作用域的服务可防止内存泄漏,全局服务可避免重复初始化
如需了解作用域、生命周期管理、结构体注入和调试内容,请查看高级用法
如需了解测试模式(克隆、覆盖、模拟),请查看测试

Quick Reference

速查参考

Registration

注册

FunctionPurpose
do.Provide[T]()
Register lazy service (default)
do.ProvideNamed[T]()
Register named lazy service
do.ProvideValue[T]()
Register pre-created value
do.ProvideNamedValue[T]()
Register named value
do.ProvideTransient[T]()
Register new instance each time
do.ProvideNamedTransient[T]()
Register named transient service
do.Package()
Group service registrations
函数用途
do.Provide[T]()
注册延迟加载服务(默认)
do.ProvideNamed[T]()
注册命名延迟加载服务
do.ProvideValue[T]()
注册预先创建的值
do.ProvideNamedValue[T]()
注册命名值
do.ProvideTransient[T]()
注册每次请求创建新实例的服务
do.ProvideNamedTransient[T]()
注册命名瞬时型服务
do.Package()
分组服务注册

Invocation

调用

FunctionPurpose
do.Invoke[T]()
Get service (with error)
do.InvokeNamed[T]()
Get named service
do.InvokeAs[T]()
Get first service matching interface
do.InvokeStruct[T]()
Inject into struct fields using tags
do.MustInvoke[T]()
Get service (panic on error)
do.MustInvokeNamed[T]()
Get named service (panic on error)
do.MustInvokeAs[T]()
Get service by interface (panic on error)
do.MustInvokeStruct[T]()
Inject into struct (panic on error)
函数用途
do.Invoke[T]()
获取服务(带错误返回)
do.InvokeNamed[T]()
获取命名服务
do.InvokeAs[T]()
获取第一个匹配接口的服务
do.InvokeStruct[T]()
使用标签注入结构体字段
do.MustInvoke[T]()
获取服务(出错时panic)
do.MustInvokeNamed[T]()
获取命名服务(出错时panic)
do.MustInvokeAs[T]()
通过接口获取服务(出错时panic)
do.MustInvokeStruct[T]()
注入结构体(出错时panic)

Cross-References

交叉参考

  • -> See
    samber/cc-skills-golang@golang-dependency-injection
    skill for DI concepts, comparison, and when to adopt a DI library
  • -> See
    samber/cc-skills-golang@golang-structs-interfaces
    skill for interface design patterns
  • -> See
    samber/cc-skills-golang@golang-testing
    skill for general testing patterns
  • -> 查看
    samber/cc-skills-golang@golang-dependency-injection
    技能,了解DI概念、对比以及何时采用DI库
  • -> 查看
    samber/cc-skills-golang@golang-structs-interfaces
    技能,了解接口设计模式
  • -> 查看
    samber/cc-skills-golang@golang-testing
    技能,了解通用测试模式