Introduction
Google Wire is a package that helps manage Dependency Injection (DI) in Go, developed by Google. Its main purpose is to generate code that wires together functions with injected values so they can work together.
Dependency Injection (DI) is the practice of providing dependencies from the outside instead of creating them inside the function. The benefits are:
- Reduced code coupling (loose coupling)
- Easier testing — since the code is decoupled, you can mock dependencies and inject them for testing easily
Example:
package main
import "fmt"
type Animal interface {
MakeSound()
}
// Dog
type Dog struct{}
func (d *Dog) MakeSound() {
fmt.Println("Woof!")
}
// Cat
type Cat struct{}
func (c *Cat) MakeSound() {
fmt.Println("Meow!")
}
type Zoo struct {
animals []Animal
}
func (z *Zoo) MakeSounds() {
for _, animal := range z.animals {
animal.MakeSound()
}
}
// NewZoo is a constructor function for Zoo with DI
func NewZoo(animals ...Animal) *Zoo {
return &Zoo{animals: animals}
}
func main() {
// Injecting dependencies
dog := &Dog{}
cat := &Cat{}
zoo := NewZoo(dog, cat) // dog and cat are injected into NewZoo
zoo.MakeSounds()
}
Core Concepts
1. Providers
These are functions that return newly created objects, for example:
type Message string
func NewMessage() Message {
return Message("Hi there!")
}
They can be grouped into what’s called a provider set, like this:
package foobarbaz
import (
// ...
"github.com/google/wire"
"errors"
)
// Foo
type Foo struct {
X int
}
func ProvideFoo() Foo {
return Foo{X: 42}
}
// Bar
type Bar struct {
X int
}
func ProvideBar(foo Foo) Bar {
return Bar{X: -foo.X}
}
// Baz
type Baz struct {
X int
}
func ProvideBaz(bar Bar) (Baz, error) {
if bar.X == 0 {
return Baz{}, errors.New("cannot provide baz when bar is zero")
}
return Baz{X: bar.X}, nil
}
// Group provider
var SuperSet = wire.NewSet(ProvideFoo, ProvideBar, ProvideBaz)
2. Injectors
These are functions that combine all provider functions together after code generation.
Example:
// +build wireinject
// The build tag ensures this stub is excluded from the final build.
package main
import (
"context"
"github.com/google/wire"
"example.com/foobarbaz"
)
// injector function
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
wire.Build(foobarbaz.SuperSet)
return foobarbaz.Baz{}, nil
}
Now that you understand providers and injectors, let’s look at how to set up and use Google Wire in practice.
Set Up and Basic Usage
1.Install the Google Wire package
go install github.com/google/wire/cmd/wire@latest
2.Create your desired provider functions, for example:
// greeter/greeter.go
package greeter
type Message string
// provider function
func NewMessage() Message {
return Message("Hi there!")
}
type Greeter struct {
Message Message
}
func (g Greeter) Greet() Message {
return g.Message
}
// provider function
func NewGreeter(m Message) Greeter {
return Greeter{Message: m}
}
type Event struct {
Greeter Greeter
}
func (e Event) Start() {
msg := e.Greeter.Greet()
fmt.Println(msg)
}
func NewEvent(g Greeter) Event {
return Event{Greeter: g}
}
var MainBindingSet = wire.NewSet(NewMessage, NewGreeter, NewEvent)
3.Create an injector function in di/wire.go
:
// di/wire.go
//go:build wireinject
// +build wireinject
package di
import (
"context"
"github.com/google/wire"
"github.com/kittichanr/go-wire/greeter"
)
func InitializeApplication(ctx context.Context) (greeter.Event, func(), error) {
wire.Build(greeter.MainBindingSet)
return greeter.Event{}, func() {}, nil
}
You must include the go:build
and +build
comments so that the Wire CLI knows where to generate code.
// go:build wireinject
// +build wireinject
4.In main.go
, call the injector function to initialize everything when running your application:
// main.go
package main
import (
"context"
"log"
"github.com/kittichanr/go-wire/di"
)
func main() {
ctx := context.Background()
_, cleanUpFn, err := di.InitializeApplication(ctx)
defer cleanUpFn()
if err != nil {
log.Fatal("initial app failed")
panic(err)
}
}
5.Run wire ./di
in your terminal to generate the code that connects all provider functions. It will produce a file called di/wire_gen.go
, which might look like this:
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package di
import (
"context"
"github.com/kittichanr/go-wire/greeter"
)
// Injectors from wire.go:
func InitializeApplication(ctx context.Context) (greeter.Event, func(), error) {
message := greeter.NewMessage()
greeterGreeter := greeter.NewGreeter(message)
event := greeter.NewEvent(greeterGreeter)
return event, func() {
}, nil
}
Note: Do not edit the generated file. Instead, update your provider functions and re-run wire
to regenerate the file.
Notice how wire_gen.go
wires all provider functions for you — no need to inject them manually. Yay!
Conclusion
Go Wire is a tool that helps generate code for dependency injection, allowing functions with dependencies to work seamlessly together. You don’t need to manually wire every dependency, and you can be confident they will be initialized in the correct order. The example above demonstrates a basic setup — check out the Go Wire GitHub for more advanced usage.
Source code example: https://github.com/kittichanr/go-wire-basic
References
- https://github.com/google/wire?tab=readme-ov-file
- https://github.com/google/wire/blob/main/docs/guide.md
- https://github.com/google/wire/blob/main/_tutorial/README.md
- https://medium.com/odds-team/%E0%B8%97%E0%B8%B3-dependency-injection-%E0%B9%83%E0%B8%99-go-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-wire-ca0fc656c286
- https://medium.com/@leelorz6/dependency-injection-%E0%B8%84%E0%B8%B7%E0%B8%AD%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3-6a1a8a2996be
Top comments (0)