Go

How do you do dependency injection in Go? Compare manual constructor wiring with google/wire and uber/fx.

Question 557MediumGo 1.22 to 1.25

Idiomatic DI in Go means constructors that accept interfaces or concrete dependencies as parameters, all wired explicitly in main. No container is needed. The dependency graph is visible, checked at compile time, and easy to follow with "go to definition".

func main() {
	cfg := config.MustLoad()
	db, err := sql.Open("pgx", cfg.DSN)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	repo := postgres.NewOrderRepo(db)
	svc := order.NewService(repo, clock.Real{})
	srv := httpapi.NewServer(svc, cfg.Addr)
	log.Fatal(srv.ListenAndServe())
}
  • google/wire: compile-time code generation. You declare providers and a wire.Build(...) injector, and wire generates exactly the manual code above. Missing or duplicate providers fail at generate time, and there is no runtime reflection. The project is considered feature-complete and changes little.
  • uber/fx: a runtime container built on reflection (dig). You get fx.Provide/fx.Invoke, lifecycle hooks (OnStart/OnStop) and modules. Graph errors show up at startup, not at compile time, and following the call flow takes more effort.

Verdict: manual wiring is right for most services. Consider wire when main.go grows to hundreds of lines of plumbing. fx suits large organizations that standardize many services with shared modules and lifecycle. Gotcha: avoid a global service locator. Keep interfaces small and define them at the consumer so tests can use fakes.

More on Go Idioms, Design Patterns & Language Design

All 16 Go Idioms, Design Patterns & Language Design questions