$npx -y skills add samber/cc-skills-golang --skill golang-samber-doDependency injection in Golang using samber/do — service containers, lifecycle management, scopes, health checks, graceful shutdown, and module organization. Apply when using or adopting samber/do, when the codebase imports github.com/samber/do or github.com/samber/do/v2, or when
| 1 | **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. |
| 2 | |
| 3 | # Using samber/do for Dependency Injection in Go |
| 4 | |
| 5 | Type-safe dependency injection toolkit for Go based on Go 1.18+ generics. |
| 6 | |
| 7 | **Official Resources:** |
| 8 | |
| 9 | - [pkg.go.dev/github.com/samber/do/v2](https://pkg.go.dev/github.com/samber/do/v2) |
| 10 | - [do.samber.dev](https://do.samber.dev) |
| 11 | - [github.com/samber/do/v2](https://github.com/samber/do) |
| 12 | |
| 13 | This skill is not exhaustive. Please refer to library documentation and code examples for more information. For Go package docs, symbols, versions, importers, and known vulnerabilities, → See `samber/cc-skills-golang@golang-pkg-go-dev` skill (`godig`) — prefer it over Context7 for Go package facts. To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See `samber/cc-skills-golang@golang-gopls` skill (`gopls`). Context7 remains a fallback for docs not indexed on pkg.go.dev. |
| 14 | |
| 15 | DO NOT USE v1 OF THIS LIBRARY. INSTALL v2 INSTEAD: |
| 16 | |
| 17 | ```bash |
| 18 | go get -u github.com/samber/do/v2 |
| 19 | ``` |
| 20 | |
| 21 | ## Core Concepts |
| 22 | |
| 23 | ### The Injector (Container) |
| 24 | |
| 25 | ```go |
| 26 | import "github.com/samber/do/v2" |
| 27 | |
| 28 | injector := do.New() |
| 29 | ``` |
| 30 | |
| 31 | ### Service Types |
| 32 | |
| 33 | - **Lazy** (default): Created when first requested |
| 34 | - **Eager**: Created immediately when the container starts |
| 35 | - **Transient**: New instance created on every request |
| 36 | - **Value**: Pre-created value, no instantiation |
| 37 | |
| 38 | ### Provider Functions |
| 39 | |
| 40 | Services MUST be registered via provider functions: |
| 41 | |
| 42 | ```go |
| 43 | type Provider[T any] func(i Injector) (T, error) |
| 44 | ``` |
| 45 | |
| 46 | ## Basic Usage |
| 47 | |
| 48 | ### 1. Define and Register Services |
| 49 | |
| 50 | Follow "Accept Interfaces, Return Structs": |
| 51 | |
| 52 | ```go |
| 53 | // Register a service (lazy by default) |
| 54 | do.Provide(injector, func(i do.Injector) (Database, error) { |
| 55 | return &PostgreSQLDatabase{connString: "postgres://..."}, nil |
| 56 | }) |
| 57 | |
| 58 | // Register a pre-created value |
| 59 | do.ProvideValue(injector, &Config{Port: 8080}) |
| 60 | |
| 61 | // Register a transient service (new instance each time) |
| 62 | do.ProvideTransient(injector, func(i do.Injector) (*Logger, error) { |
| 63 | return &Logger{}, nil |
| 64 | }) |
| 65 | |
| 66 | // Register an eager service (created immediately at startup) |
| 67 | do.ProvideValue(injector, &Config{Port: 8080}) |
| 68 | ``` |
| 69 | |
| 70 | ### 2. Invoke Services |
| 71 | |
| 72 | The container MUST only be accessed at the composition root: |
| 73 | |
| 74 | ```go |
| 75 | // Invoke with error handling |
| 76 | db, err := do.Invoke[Database](injector) |
| 77 | |
| 78 | // MustInvoke panics on error (use when confident service exists) |
| 79 | db := do.MustInvoke[Database](injector) |
| 80 | ``` |
| 81 | |
| 82 | ### 3. Service Dependencies |
| 83 | |
| 84 | ```go |
| 85 | func NewUserService(i do.Injector) (UserService, error) { |
| 86 | db := do.MustInvoke[Database](i) |
| 87 | cache := do.MustInvoke[Cache](i) |
| 88 | return &userService{db: db, cache: cache}, nil |
| 89 | } |
| 90 | |
| 91 | do.Provide(injector, NewUserService) |
| 92 | ``` |
| 93 | |
| 94 | ### 4. Implicit Aliasing (Preferred) |
| 95 | |
| 96 | Register a concrete type and invoke as an interface without explicit aliasing: |
| 97 | |
| 98 | ```go |
| 99 | // Register concrete type |
| 100 | do.Provide(injector, func(i do.Injector) (*PostgreSQLDatabase, error) { |
| 101 | return &PostgreSQLDatabase{}, nil |
| 102 | }) |
| 103 | |
| 104 | // Invoke directly as interface (implicit aliasing) |
| 105 | db := do.MustInvokeAs[Database](injector) |
| 106 | ``` |
| 107 | |
| 108 | ### 5. Named Services |
| 109 | |
| 110 | Register multiple services of the same type: |
| 111 | |
| 112 | ```go |
| 113 | do.ProvideNamed(injector, "primary-db", func(i do.Injector) (*Database, error) { |
| 114 | return &Database{URL: "postgres://primary..."}, nil |
| 115 | }) |
| 116 | |
| 117 | mainDB := do.MustInvokeNamed[*Database](injector, "primary-db") |
| 118 | ``` |
| 119 | |
| 120 | ## Package Organization |
| 121 | |
| 122 | Use `do.Package()` to organize service registration by module: |
| 123 | |
| 124 | ```go |
| 125 | // infrastructure/package.go |
| 126 | var Package = do.Package( |
| 127 | do.Lazy(func(i do.Injector) (*postgres.DB, error) { |
| 128 | cfg := do.MustInvoke[*Config](i) |
| 129 | return postgres.Connect(cfg.DatabaseURL) |
| 130 | }), |
| 131 | do.Lazy(func(i do.Injector) (*redis.Client, error) { |
| 132 | cfg := do.MustInvoke[*Config](i) |
| 133 | return redis.NewClient(cfg.RedisURL), nil |
| 134 | }), |
| 135 | ) |
| 136 | |
| 137 | // main.go |
| 138 | injector := do.New(infrastructure.Package, service.Package) |
| 139 | ``` |
| 140 | |
| 141 | ## Full Application Setup |
| 142 | |
| 143 | ```go |
| 144 | func main() { |
| 145 | inj |