← Voltar para o blog

Injeção de Dependência em Go com Wire e Fx

Domine injeção de dependência em Go com Google Wire e Uber Fx. Compare code generation vs runtime DI, veja exemplos práticos e escolha a melhor abordagem.

Injeção de dependência (DI) é um dos padrões mais importantes para escrever código Go testável e manutenível. Em vez de criar dependências dentro de uma função, você as recebe como parâmetros — o que facilita testes, desacopla módulos e torna a arquitetura limpa viável na prática.

Go não tem um framework de DI obrigatório como Spring (Java), mas existem duas ferramentas maduras que resolvem o problema de formas diferentes: Google Wire (geração de código) e Uber Fx (container em runtime). Neste guia, exploramos ambas com exemplos prontos para usar.

Por que Injeção de Dependência Importa em Go

Considere um serviço de pedidos que depende de um repositório e um serviço de notificação:

// SEM injeção de dependência — acoplamento forte
type PedidoService struct{}

func (s *PedidoService) CriarPedido(p Pedido) error {
	repo := &PostgresRepo{} // dependência hardcoded
	notif := &EmailNotifier{} // impossível trocar em testes
	repo.Salvar(p)
	notif.Enviar(p.Email, "Pedido criado")
	return nil
}

O problema: você não consegue testar CriarPedido sem um banco PostgreSQL real e um servidor de e-mail. Com DI:

// COM injeção de dependência — desacoplado e testável
type PedidoService struct {
	repo  PedidoRepository
	notif Notificador
}

func NewPedidoService(repo PedidoRepository, notif Notificador) *PedidoService {
	return &PedidoService{repo: repo, notif: notif}
}

func (s *PedidoService) CriarPedido(p Pedido) error {
	if err := s.repo.Salvar(p); err != nil {
		return fmt.Errorf("salvar pedido: %w", err)
	}
	return s.notif.Enviar(p.Email, "Pedido criado")
}

Agora em testes, basta injetar mocks que implementam as interfaces. Mas em projetos grandes, a fiação manual dessas dependências fica trabalhosa — e é aí que Wire e Fx entram.

DI Manual vs Assistida por Framework

Em projetos pequenos, a DI manual funciona bem:

func main() {
	db := database.Connect(os.Getenv("DATABASE_URL"))
	repo := repository.NewPostgres(db)
	notif := notification.NewEmail(os.Getenv("SMTP_HOST"))
	service := pedido.NewPedidoService(repo, notif)
	handler := api.NewHandler(service)
	http.ListenAndServe(":8080", handler)
}

Mas quando seu projeto tem 20, 30 ou 50 dependências com relações hierárquicas, a main() vira um emaranhado de New*() calls. Wire e Fx automatizam essa fiação.

Google Wire: Geração de Código

Wire é a abordagem idiomática de Go para DI — resolve tudo em tempo de compilação via geração de código. Sem reflexão, sem mágica em runtime.

Instalação

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

Definindo Providers

Cada função construtora (New*) é um provider — ela declara o que precisa e o que produz:

// internal/repository/postgres.go
package repository

import "database/sql"

type PostgresRepo struct {
	db *sql.DB
}

func NewPostgresRepo(db *sql.DB) *PostgresRepo {
	return &PostgresRepo{db: db}
}
// internal/service/pedido.go
package service

type PedidoService struct {
	repo  PedidoRepository
	notif Notificador
}

func NewPedidoService(repo PedidoRepository, notif Notificador) *PedidoService {
	return &PedidoService{repo: repo, notif: notif}
}

Definindo o Injector

Crie um arquivo wire.go com a função injector:

//go:build wireinject
// +build wireinject

package main

import "github.com/google/wire"

func InitializeApp() (*App, error) {
	wire.Build(
		database.NewConnection,
		repository.NewPostgresRepo,
		wire.Bind(new(service.PedidoRepository), new(*repository.PostgresRepo)),
		notification.NewEmailNotifier,
		wire.Bind(new(service.Notificador), new(*notification.EmailNotifier)),
		service.NewPedidoService,
		api.NewHandler,
		NewApp,
	)
	return nil, nil
}

Execute wire para gerar o código de fiação:

wire ./...

Wire gera um wire_gen.go com toda a fiação explícita — código Go puro que você pode inspecionar e depurar como qualquer outro código. Sem reflexão, sem containers ocultos.

Agrupando com ProviderSets

Para projetos grandes, agrupe providers relacionados:

package repository

var Set = wire.NewSet(
	NewPostgresRepo,
	wire.Bind(new(service.PedidoRepository), new(*PostgresRepo)),
)
// wire.go — fica muito mais limpo
wire.Build(
	database.Set,
	repository.Set,
	notification.Set,
	service.Set,
	api.Set,
	NewApp,
)

Uber Fx: Container em Runtime

Fx é a abordagem de DI do Uber — resolve dependências em runtime usando reflexão. Mais flexível que Wire, mas com overhead em tempo de execução.

Instalação

go get go.uber.org/fx

Aplicação Básica com Fx

package main

import (
	"context"
	"net/http"

	"go.uber.org/fx"
)

func main() {
	app := fx.New(
		fx.Provide(
			database.NewConnection,
			repository.NewPostgresRepo,
			notification.NewEmailNotifier,
			service.NewPedidoService,
			api.NewHandler,
		),
		fx.Invoke(startServer),
	)
	app.Run()
}

func startServer(lc fx.Lifecycle, handler *api.Handler) {
	srv := &http.Server{Addr: ":8080", Handler: handler}

	lc.Append(fx.Hook{
		OnStart: func(ctx context.Context) error {
			go srv.ListenAndServe()
			return nil
		},
		OnStop: func(ctx context.Context) error {
			return srv.Shutdown(ctx)
		},
	})
}

Fx automaticamente resolve o grafo de dependências em runtime. fx.Provide registra construtores, fx.Invoke executa funções que consomem dependências.

Lifecycle Hooks

Um diferencial do Fx é o gerenciamento de ciclo de vida. Conexões de banco, consumers Kafka e servidores HTTP são iniciados e finalizados de forma ordenada:

func NewKafkaConsumer(lc fx.Lifecycle, cfg Config) *kafka.Consumer {
	consumer := kafka.NewConsumer(cfg.Brokers)

	lc.Append(fx.Hook{
		OnStart: func(ctx context.Context) error {
			go consumer.Start()
			return nil
		},
		OnStop: func(ctx context.Context) error {
			return consumer.Close()
		},
	})

	return consumer
}

Isso é especialmente útil em microsserviços com múltiplas conexões e workers.

Interfaces com Fx

Para injetar interfaces em vez de tipos concretos:

fx.Provide(
	fx.Annotate(
		repository.NewPostgresRepo,
		fx.As(new(service.PedidoRepository)),
	),
)

Wire vs Fx: Comparação

AspectoWireFx
ResoluçãoCompile-timeRuntime
PerformanceZero overheadReflexão na inicialização
DebugCódigo gerado legívelStack traces mais complexas
LifecycleManualGerenciado (hooks start/stop)
Curva de aprendizadoModeradaModerada
Erros de fiaçãoCompilação falhaPanic na inicialização
Projeto idealBibliotecas, CLIs, APIs simplesMicrosserviços complexos

Quando usar Wire

  • Você quer zero overhead em runtime
  • Precisa de código gerado inspecionável
  • O projeto é uma API, CLI ou ferramenta de linha de comando
  • A equipe prefere abordagens idiomáticas de Go

Quando usar Fx

  • O projeto tem muitas dependências com ciclo de vida complexo
  • Você precisa de graceful shutdown orquestrado
  • A equipe já usa Fx em outros serviços (padrão Uber)
  • Microsserviços com Kafka, gRPC e múltiplos workers

Benefícios para Testes

DI — seja manual, Wire ou Fx — transforma a testabilidade do código. Com interfaces bem definidas, seus testes ficam isolados:

func TestCriarPedido(t *testing.T) {
	mockRepo := &MockRepo{}
	mockNotif := &MockNotifier{}
	svc := service.NewPedidoService(mockRepo, mockNotif)

	err := svc.CriarPedido(Pedido{Email: "test@example.com"})

	if err != nil {
		t.Fatalf("erro inesperado: %v", err)
	}
	if !mockRepo.SalvarCalled {
		t.Error("Salvar não foi chamado")
	}
}

Para mais sobre testes em Go, incluindo fuzzing e testes de integração com Testcontainers, confira nossos guias dedicados.

Estrutura de Projeto Real

Uma estrutura que funciona bem com DI:

meu-servico/
├── cmd/
│   └── api/
│       ├── main.go         # Fiação (Wire injector ou Fx app)
│       └── wire.go          # (Se usar Wire)
├── internal/
│   ├── domain/              # Entidades e interfaces
│   │   ├── pedido.go
│   │   └── repository.go
│   ├── repository/          # Implementações de persistência
│   │   └── postgres.go
│   ├── service/             # Lógica de negócio
│   │   └── pedido.go
│   └── api/                 # Handlers HTTP/gRPC
│       └── handler.go
├── go.mod
└── go.sum

Essa organização segue os princípios de clean architecture com separação clara entre domínio, infraestrutura e API. O uso de context para propagação de dados e cancelamento complementa a DI para criar serviços robustos.

Para logging dentro dos serviços injetados, use slog configurado no nível da aplicação e injetado como dependência. Para observabilidade, injete o tracer do OpenTelemetry da mesma forma.

Outras linguagens também resolvem DI de formas interessantes: Kotlin com Koin e Dagger, e Python com dependency-injector — mas a filosofia de Go com Wire prioriza simplicidade e transparência via geração de código.

FAQ

Preciso de um framework de DI em Go?

Não necessariamente. Para projetos pequenos e médios, a DI manual (passando dependências nos construtores) é simples e eficaz. Frameworks como Wire e Fx se justificam quando o grafo de dependências tem mais de 15-20 componentes e a fiação manual se torna difícil de manter.

Wire gera código que precisa ser commitado?

Sim. O arquivo wire_gen.go gerado deve ser commitado no repositório. Ele é código Go válido que qualquer pessoa pode ler e entender. Execute wire ./... sempre que alterar os providers e commit o resultado.

Fx tem impacto na performance da aplicação?

O impacto de Fx é apenas na inicialização — a resolução de dependências por reflexão acontece uma vez quando a aplicação inicia. Em runtime, os serviços já estão instanciados e não há overhead adicional. Para a maioria das aplicações, essa latência de startup é negligível.

Posso misturar Wire e Fx no mesmo projeto?

Tecnicamente sim, mas não é recomendado. Escolha uma abordagem e mantenha consistência. Se você tem microsserviços diferentes, cada um pode usar a ferramenta mais adequada, mas dentro de um mesmo serviço, mantenha uma única estratégia de DI.

Como faço DI com generics em Go?

Com generics (Go 1.18+), você pode criar repositories e services tipados. Wire suporta generics a partir do Go 1.18, e Fx trabalha com tipos concretos instanciados. Generics complementam DI mas não substituem — são ferramentas para problemas diferentes.