golang-samber-do

golang-samber-do

熱門

在 Golang 中使用 samber/do 進行依賴注入 — 服務容器、生命週期管理、作用域、健康檢查、優雅關閉與模組組織。適用於使用或採用 samber/do 時、程式碼庫匯入 github.com/samber/do 或 github.com/samber/do/v2 時,或將手動建構子注入重構為 DI 容器時。

2261星標
150分支
更新於 2026/6/6
SKILL.md
readonlyread-only
name
golang-samber-do
description

在 Golang 中使用 samber/do 進行依賴注入 — 服務容器、生命週期管理、作用域、健康檢查、優雅關閉與模組組織。適用於使用或採用 samber/do 時、程式碼庫匯入 github.com/samber/do 或 github.com/samber/do/v2 時,或將手動建構子注入重構為 DI 容器時。

角色設定: 你是一位正在建立依賴注入的 Go 架構師。你將容器保留在組合根(composition root),依賴介面而非具體型別,並將提供者錯誤視為第一級失敗。

在 Go 中使用 samber/do 進行依賴注入

基於 Go 1.18+ 泛型的型別安全依賴注入工具組。

官方資源:

此技能並非詳盡無遺。請參考函式庫文件與程式碼範例以取得更多資訊。Context7 可作為探索平台提供協助。如需 Go 套件文件、版本、符號與已知漏洞,請參閱 samber/cc-skills-golang@golang-pkg-go-dev 技能。

請勿使用此函式庫的 v1 版本。請安裝 v2:

go get -u github.com/samber/do/v2

核心概念

注入器(容器)

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

injector := do.New()

服務型別

  • Lazy(預設):首次請求時建立
  • Eager:容器啟動時立即建立
  • Transient:每次請求都建立新實例
  • Value:預先建立的值,無需實例化

提供者函式

服務必須透過提供者函式註冊:

type Provider[T any] func(i Injector) (T, error)

基本用法

1. 定義與註冊服務

遵循「接受介面,回傳結構」:

// 註冊服務(預設為 lazy)
do.Provide(injector, func(i do.Injector) (Database, error) {
    return &PostgreSQLDatabase{connString: "postgres://..."}, nil
})

// 註冊預先建立的值
do.ProvideValue(injector, &Config{Port: 8080})

// 註冊 transient 服務(每次新實例)
do.ProvideTransient(injector, func(i do.Injector) (*Logger, error) {
    return &Logger{}, nil
})

// 註冊 eager 服務(啟動時立即建立)
do.ProvideValue(injector, &Config{Port: 8080})

2. 調用服務

容器只能在組合根(composition root)存取:

// 調用並處理錯誤
db, err := do.Invoke[Database](injector)

// MustInvoke 在錯誤時 panic(確定服務存在時使用)
db := do.MustInvoke[Database](injector)

3. 服務依賴

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. 隱式別名(建議)

註冊具體型別,並以介面調用,無需明確別名:

// 註冊具體型別
do.Provide(injector, func(i do.Injector) (*PostgreSQLDatabase, error) {
    return &PostgreSQLDatabase{}, nil
})

// 直接以介面調用(隱式別名)
db := do.MustInvokeAs[Database](injector)

5. 具名服務

註冊多個相同型別的服務:

do.ProvideNamed(injector, "primary-db", func(i do.Injector) (*Database, error) {
    return &Database{URL: "postgres://primary..."}, nil
})

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

套件組織

使用 do.Package() 按模組組織服務註冊:

// 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)

完整應用程式設定

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)
}

最佳實務

  1. 依賴介面,而非具體型別 — 讓你在測試中無需修改正式程式碼即可替換實作
  2. 每個服務應只有一個職責 — 多職責的服務更難測試與替換
  3. 保持依賴樹淺層 — 超過 3-4 層的鏈條會使初始化順序脆弱,錯誤更難追蹤
  4. 在提供者函式中處理錯誤 — 默默失敗的提供者會產生損壞的服務,在後續意想不到的地方崩潰
  5. 使用作用域按生命週期組織服務 — 請求作用域的服務防止洩漏,全域服務防止重複初始化

如需作用域、生命週期管理、結構注入與除錯,請參閱進階用法

如需測試模式(複製、覆寫、模擬),請參閱測試

快速參考

註冊

函式 用途
do.Provide[T]() 註冊 lazy 服務(預設)
do.ProvideNamed[T]() 註冊具名 lazy 服務
do.ProvideValue[T]() 註冊預先建立的值
do.ProvideNamedValue[T]() 註冊具名值
do.ProvideTransient[T]() 註冊每次新實例的服務
do.ProvideNamedTransient[T]() 註冊具名 transient 服務
do.Package() 將服務註冊分組

調用

函式 用途
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)

交叉參考

  • → 請參閱 samber/cc-skills-golang@golang-dependency-injection 技能了解 DI 概念、比較與何時採用 DI 函式庫
  • → 請參閱 samber/cc-skills-golang@golang-structs-interfaces 技能了解介面設計模式
  • → 請參閱 samber/cc-skills-golang@golang-testing 技能了解一般測試模式