Labsco
samber logo

golang-uber-dig

2,400

by samber · part of samber/cc-skills-golang

Implements dependency injection in Golang using uber-go/dig — reflection-based container, Provide/Invoke, dig.In/dig.Out parameter and result objects, named values, value groups, optional dependencies, scopes, and Decorate. Apply when using or adopting uber-go/dig, when the codebase imports `go.uber.org/dig`, or when wiring an application graph at startup. For higher-level lifecycle and modules, see `samber/cc-skills-golang@golang-uber-fx` skill.

🔥🔥🔥✓ VerifiedFreeAdvanced setup
🧩 One of 7 skills in the samber/cc-skills-golang package — works on its own, and pairs well with its siblings.

Implements dependency injection in Golang using uber-go/dig — reflection-based container, Provide/Invoke, dig.In/dig.Out parameter and result objects, named values, value groups, optional dependencies, scopes, and Decorate. Apply when using or adopting uber-go/dig, when the codebase imports `go.uber.org/dig`, or when wiring an application graph at startup. For higher-level lifecycle and modules, see `samber/cc-skills-golang@golang-uber-fx` skill.

Inspect the full instructions your agent will receiveExpand

This is the exact playbook injected into your agent when the skill activates — shown here so you can audit it before installing. You don't need to read it to use the skill.

by samber

Implements dependency injection in Golang using uber-go/dig — reflection-based container, Provide/Invoke, dig.In/dig.Out parameter and result objects, named values, value groups, optional dependencies, scopes, and Decorate. Apply when using or adopting uber-go/dig, when the codebase imports go.uber.org/dig, or when wiring an application graph at startup. For higher-level lifecycle and modules, see samber/cc-skills-golang@golang-uber-fx skill. npx skills add https://github.com/samber/cc-skills-golang --skill golang-uber-dig Download ZIPGitHub2.4k Persona: You are a Go architect wiring an application graph with dig. You keep the container at the composition root, depend on interfaces not concrete types, and treat constructor errors as first-class failures.

Using uber-go/dig for Dependency Injection in Go

Reflection-based DI toolkit, designed to power application frameworks (it is the engine behind uber-go/fx) and resolve object graphs during startup.

Official Resources:

This skill is not exhaustive. Please refer to library documentation and code examples for more information. Context7 can help as a discoverability platform. For Go package docs, versions, symbols, and known vulnerabilities, → See samber/cc-skills-golang@golang-pkg-go-dev skill.

Copy & paste — that's it
go get go.uber.org/dig

dig vs. fx

fx is built on dig and shares the same container engine — the DI primitives (Provide, Invoke, In/Out structs, named values, value groups) are identical. fx.In/fx.Out are re-exports of dig.In/dig.Out.

What fx adds on top of dig:

Concern dig fx DI container ✅ dig.New() ✅ (embedded) Lifecycle hooks ❌ ✅ fx.Lifecycle OnStart/OnStop Module system ❌ ✅ fx.Module with scoped decorators Signal-aware run loop ❌ ✅ app.Run() blocks on SIGINT/SIGTERM Structured event logging ❌ ✅ fx.WithLogger / fxevent Startup/shutdown timeout ❌ ✅ fx.StartTimeout / fx.StopTimeout

Choose dig when you need the wiring graph only: CLI tools, libraries exposing a container to callers, test harnesses, or embedding DI into an existing app that manages its own lifecycle.

Choose fx for long-running services (HTTP servers, workers, daemons) — lifecycle and signal handling are non-negotiable there. See samber/cc-skills-golang@golang-uber-fx skill.

Container

Copy & paste — that's it
import "go.uber.org/dig"

c := dig.New()

Useful options: dig.DeferAcyclicVerification() (faster startup), dig.RecoverFromPanics() (turn panics into dig.PanicError), dig.DryRun(true) (validate without invoking).

Provide and Invoke

Copy & paste — that's it
// Register a constructor — lazy, only runs when its output is needed
err := c.Provide(func(cfg *Config) (*sql.DB, error) {
 return sql.Open("postgres", cfg.DSN)
})

// Pull a service out of the container by asking for it as a function parameter
err = c.Invoke(func(db *sql.DB) error {
 return db.Ping()
})

Constructors are lazy and memoized: each output type is built once and shared (singleton per container). Provide errors at registration if the constructor is malformed; Invoke returns the constructor's error wrapped with the dependency path that triggered it.

A dig constructor is any function. Inputs are dependencies, outputs are provided types. error (last return) signals construction failure. Follow "accept interfaces, return structs".

Parameter Objects with dig.In

Once a constructor has 4+ dependencies, embed dig.In to group them as struct fields and tag fields:

Copy & paste — that's it
type HandlerParams struct {
 dig.In

 Logger *zap.Logger
 DB *sql.DB
 Cache *redis.Client `optional:"true"` // zero value if not provided
 DBRO *sql.DB `name:"readonly"` // named dependency
 Routes []http.Handler `group:"routes"` // value group
}

func NewHandler(p HandlerParams) *Handler { /* ... */ }

Tags: name:"...", optional:"true", group:"...".

Result Objects with dig.Out

Return several values from one constructor and attach name/group tags to results:

Copy & paste — that's it
type ConnResult struct {
 dig.Out

 ReadWrite *sql.DB `name:"primary"`
 ReadOnly *sql.DB `name:"readonly"`
}

func NewConnections(cfg *Config) (ConnResult, error) { /* ... */ }

Named Values

Two providers of the same type collide. Disambiguate with dig.Name:

Copy & paste — that's it
c.Provide(NewPrimaryDB, dig.Name("primary"))
c.Provide(NewReadOnlyDB, dig.Name("readonly"))

Consume by adding name:"primary" / name:"readonly" to a dig.In field.

Value Groups

Many providers, one consumer slice — typical for HTTP handlers, health checks, migrations:

Copy & paste — that's it
type RouteResult struct {
 dig.Out
 Handler http.Handler `group:"routes"`
}

func NewUserHandler(db *sql.DB) RouteResult { /* ... */ }
func NewPostHandler(db *sql.DB) RouteResult { /* ... */ }

type ServerParams struct {
 dig.In
 Routes []http.Handler `group:"routes"`
}

Flatten — append ,flatten (e.g. group:"routes,flatten") to unwrap a slice instead of nesting it. Group order is not guaranteed; if order matters, provide an explicit ordered slice from a single constructor.

Provide as Interface (dig.As)

Register a concrete constructor and expose it under one or more interfaces without a separate adapter:

Copy & paste — that's it
c.Provide(NewPostgresDB, dig.As(new(Database), new(io.Closer)))
// Consumers ask for Database or io.Closer; *PostgresDB stays hidden.

Full Application Example

Copy & paste — that's it
func main() {
 c := dig.New()

 must(c.Provide(NewConfig))
 must(c.Provide(NewLogger))
 must(c.Provide(NewDatabase))
 must(c.Provide(NewServer))

 err := c.Invoke(func(srv *http.Server) error {
 return srv.ListenAndServe()
 })
 if err != nil {
 log.Fatal(err)
 }
}

func must(err error) { if err != nil { panic(err) } }

dig has no built-in lifecycle. If you need OnStart/OnStop hooks, signal handling, and graceful shutdown, use fx — see samber/cc-skills-golang@golang-uber-fx skill.

For Decorate, Scopes, optional deps, error helpers, and Visualize, see advanced.md.

Best Practices

  • Keep the container at the composition root — never pass *dig.Container as a parameter; treat it like a plumbing detail of main(). Service-locator patterns defeat the testability gains of DI.

  • Depend on interfaces, not concrete types — lets you swap implementations in tests without touching production code, and lets you use dig.As to expose narrow interfaces from wide structs.

  • Prefer parameter objects (dig.In structs) once a constructor has 4+ dependencies — call sites stay readable and adding a new dependency is a one-line change instead of a signature break.

  • Group registration by module (one file per module that calls c.Provide for its types) — review and refactoring become a per-module concern, and you can extract a module into a fx.Module later without rewriting wiring.

  • Validate the graph eagerly in tests — call c.Invoke against the composition root in CI to surface missing providers at boot time, not at first request. DryRun(true) skips constructor execution.

  • Return errors from constructors instead of panicking — dig wraps them with the dependency path, which makes the failure point obvious.

Testing

dig containers are cheap — build a fresh one per test, override providers with Decorate, and call Invoke to drive the system. For full patterns (per-test wiring, shared helpers, graph validation in CI, asserting wire-time errors, recovering from constructor panics), see testing.md.

Further Reading

  • advanced.md — Decorate, Scopes, optional deps, error helpers, Visualize, full Quick Reference

  • recipes.md — end-to-end examples: HTTP server with route group, two databases, request scopes, decorators, dry-run validation

  • testing.md — testing patterns and graph validation

Cross-References

  • → See samber/cc-skills-golang@golang-uber-fx skill for application lifecycle, modules, and signal-aware Run() built on top of dig

  • → See samber/cc-skills-golang@golang-dependency-injection skill for DI concepts and library comparison

  • → See samber/cc-skills-golang@golang-samber-do skill for a generics-based alternative without reflection

  • → See samber/cc-skills-golang@golang-google-wire skill for compile-time DI (no runtime container)

  • → 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

If you encounter a bug or unexpected behavior in uber-go/dig, open an issue at https://github.com/uber-go/dig/issues.