Dependency Injection Container for Go (Golang): Wire, Dig, and Uber FX

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Dependency Injection Container for Go (Golang): Wire, Dig, and Uber FX



Dependency Injection (DI) is a design pattern where an object receives its dependencies from the outside, rather than creating them itself. In Go (Golang), DI is especially useful for creating modular, testable, and scalable code. The most popular DI tools for Go are Google Wire, Uber Dig, and Uber FX. In this article, we will look at each of them, their features, and usage examples.



Why is Dependency Injection needed in Go?

Go is a statically typed language with explicit parameter passing. Without DI, developers often create global variables or factories, which complicates testing and maintenance. DI containers solve these problems:

  • Loose coupling — components depend on interfaces, not concrete implementations.
  • Testability — it's easy to substitute dependencies with mocks.
  • Simplified configuration — the container automatically resolves the dependency graph.


Google Wire — Code Generation at Compile Time

Wire from Google is not a runtime container, but a code generator. It analyzes constructor functions and generates code for wiring dependencies at compile time. This results in zero runtime overhead and early error detection.



Installing Wire

go install github.com/google/wire/cmd/wire@latest


Example of Using Wire

Suppose we have a service that depends on a database and a logger:

// main.gopackage main

import "fmt"

type Logger struct{}

func NewLogger() Logger { return Logger{} }

func (l Logger) Log(msg string) { fmt.Println(msg) }

type Database struct { logger Logger}

func NewDatabase(logger Logger) Database { return Database{logger: logger}}

type Service struct { db Database log Logger}

func NewService(db Database, log Logger) Service { return Service{db: db, log: log}}

Now let's create a wire.go file with instructions for Wire:

// wire.go//go:build wireinject// +build wireinject

package main

import "github.com/google/wire"

func InitializeService() Service { wire.Build(NewLogger, NewDatabase, NewService) return Service{}}

Run the generation:

wire

Wire will create a wire_gen.go file with the ready initialization code:

// wire_gen.go// Code generated by Wire. DO NOT EDIT.

package main

func InitializeService() Service { logger := NewLogger() database := NewDatabase(logger) service := NewService(database, logger) return service}

Pros of Wire:

  • Errors at compile time.
  • No reflection, high performance.
  • Simple and predictable code.

Cons of Wire:

  • Requires a separate generation step.
  • Difficult to use with dynamic dependencies.


Uber Dig — Runtime Container with Reflection

Dig from Uber is a container that works at runtime. It uses reflection to analyze types and automatically resolve dependencies. Dig provides a simple API for registering constructors and retrieving objects.



Installing Dig

go get go.uber.org/dig


Example of Using Dig

package main

import ( "fmt" "go.uber.org/dig")

type Config struct { Port int}

func NewConfig() *Config { return &Config{Port: 8080}}

type Server struct { config *Config}

func NewServer(c *Config) *Server { return &Server{config: c}}

func (s *Server) Start() { fmt.Print

Recommendations