---
title: "Google Wire vs Uber Fx em Go: DI com Código Gerado ou Runtime"
url: "https://golang.com.br/blog/google-wire-vs-uber-fx-go-dependency-injection/"
markdown_url: "https://golang.com.br/blog/google-wire-vs-uber-fx-go-dependency-injection.MD"
description: "Compare Google Wire e Uber Fx em Go: compile-time vs runtime, lifecycle, módulos, testes, grafo de dependências e quando ficar só com construtores manuais."
date: "2026-08-04"
author: "Golang Brasil"
---

# Google Wire vs Uber Fx em Go: DI com Código Gerado ou Runtime

Compare Google Wire e Uber Fx em Go: compile-time vs runtime, lifecycle, módulos, testes, grafo de dependências e quando ficar só com construtores manuais.


**Para a maioria dos serviços Go em 2026, comece sem framework de DI.** Construtores explícitos, interfaces pequenas e um `main` que monta o grafo continuam sendo o caminho idiomático — e o que descrevemos no guia de [dependency injection em Go sem framework](/blog/dependency-injection-go-sem-framework/). Quando o grafo passa de dezenas de componentes, a pergunta muda: **Google Wire** (código gerado em compile-time) ou **Uber Fx** (container e lifecycle em runtime)?

A resposta curta: **Wire** se você quer manter DI “como código Go” e falhar no build; **Fx** se você precisa de módulos reutilizáveis, `OnStart`/`OnStop` e composição de aplicação em escala. Este artigo compara os dois com código, critérios de decisão, armadilhas e o momento certo de **não** adotar nenhum deles. Complementa [Clean Architecture em Go](/blog/clean-architecture-go-sem-overengineering/), [graceful shutdown](/blog/graceful-shutdown-go-producao/), [configuração com Viper](/blog/configuracao-go-viper-variaveis-ambiente/), [slog](/blog/slog-go-logging-estruturado/) e [mocks em Go](/blog/mocks-go-testify-gomock-fakes-httptest/).

## Resposta rápida: Wire, Fx ou só construtores?

| Situação | Recomendação |
|---|---|
| API/worker com poucos serviços e 1–2 repositórios | **Construtores manuais** |
| Grafo grande, mas time quer zero runtime magic | **Google Wire** |
| Muitos módulos de infra compartilhados entre serviços | **Uber Fx** |
| Lifecycle complexo (HTTP + workers + filas + cron) | **Fx** (ou main manual bem estruturado) |
| Biblioteca open source que outros vão importar | **Sem Wire/Fx na API pública** |
| Time júnior ou onboarding constante | Prefira **manual ou Wire** (mais legível no debugger) |
| “Preciso de DI porque vi no Spring/Nest” | **Não adote ainda** — modele interfaces primeiro |

**Regra prática:** se o `main` ainda cabe em uma tela e cada teste monta fakes com três linhas, você não tem problema de DI — tem problema de escopo de feature. Ferramenta de DI não substitui [interfaces pequenas](/aprenda/golang-interfaces/) nem [testes de tabela](/blog/testes-tabela-go-guia-table-driven-tests/).

## O que cada ferramenta realmente resolve

As duas resolvem o mesmo incômodo: **montar o grafo de dependências sem copiar e colar `NewService(...)` em cinco `main`s**. A diferença está em *quando* e *como* o grafo é resolvido.

| Critério | Construtores manuais | Google Wire | Uber Fx |
|---|---|---|---|
| Momento da montagem | Compile-time (seu código) | Compile-time (código gerado) | Runtime |
| Falha de grafo | Erro de compilação / nil | Erro no `wire` / build | Panic ou erro no `fx.New` |
| Lifecycle (`Start`/`Stop`) | Você implementa | Você implementa | Embutido (`OnStart`/`OnStop`) |
| Módulos reutilizáveis | Pacotes + funções `New*` | Provider sets | `fx.Module` / `fx.Options` |
| Reflexão em runtime | Não | Não | Sim (parcial) |
| Curva de aprendizado | Baixa | Média | Alta |
| Debug no IDE | Excelente | Bom (código gerado legível) | Mais indirection |
| Acoplamento | Zero | `//go:build wireinject` | Import de `go.uber.org/fx` no bootstrap |
| Ideal para | Serviços pequenos/médios | Grafo grande e explícito | Plataformas multi-módulo |

Nenhuma linha dessa tabela autoriza pular o desenho das dependências. Wire e Fx **não inventam** boas interfaces; eles apenas montam o que você já declarou.

## Baseline: o grafo manual que todo time deveria dominar

Antes de Wire ou Fx, o padrão saudável parece com isto:

```go
package main

import (
	"context"
	"log/slog"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"example.com/app/internal/config"
	"example.com/app/internal/httpapi"
	"example.com/app/internal/pedido"
	"example.com/app/internal/postgres"
)

func main() {
	cfg, err := config.Load()
	if err != nil {
		slog.Error("config", "err", err)
		os.Exit(1)
	}

	logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))

	db, err := postgres.Open(cfg.DatabaseURL)
	if err != nil {
		logger.Error("db", "err", err)
		os.Exit(1)
	}
	defer db.Close()

	repo := postgres.NewPedidoRepository(db)
	svc := pedido.NewService(repo, logger)
	handler := httpapi.NewHandler(svc, logger)

	srv := &http.Server{
		Addr:              cfg.HTTPAddr,
		Handler:           handler.Routes(),
		ReadHeaderTimeout: 5 * time.Second,
	}

	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	go func() {
		logger.Info("http listening", "addr", cfg.HTTPAddr)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			logger.Error("http", "err", err)
			stop()
		}
	}()

	<-ctx.Done()
	shutdownCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	_ = srv.Shutdown(shutdownCtx)
}
```

Esse bootstrap é legível, testável e alinhado com [graceful shutdown em produção](/blog/graceful-shutdown-go-producao/). O problema aparece quando surgem cache Redis, cliente de antifraude, publisher de eventos, worker de outbox, health checks e três repositórios — e o `main` vira um script de 200 linhas com ordem de criação frágil.

## Google Wire: DI em compile-time

[Wire](https://github.com/google/wire) é um **gerador de código**. Você declara *providers* (funções que constroem dependências) e um *injector* (função com corpo vazio e a diretiva `wire.Build`). O comando `wire` gera o `wire_gen.go` com a montagem real.

### Provider e injector

```go
//go:build wireinject
// +build wireinject

package main

import (
	"github.com/google/wire"

	"example.com/app/internal/config"
	"example.com/app/internal/httpapi"
	"example.com/app/internal/pedido"
	"example.com/app/internal/postgres"
)

func initializeApp() (*App, error) {
	wire.Build(
		config.Load,
		postgres.Open,
		postgres.NewPedidoRepository,
		pedido.NewService,
		httpapi.NewHandler,
		NewApp,
	)
	return nil, nil
}
```

O gerador produz algo no espírito de:

```go
// Code generated by Wire. DO NOT EDIT.
func initializeApp() (*App, error) {
	cfg, err := config.Load()
	if err != nil {
		return nil, err
	}
	db, err := postgres.Open(cfg.DatabaseURL)
	if err != nil {
		return nil, err
	}
	repo := postgres.NewPedidoRepository(db)
	logger := newLogger() // se estiver no provider set
	svc := pedido.NewService(repo, logger)
	handler := httpapi.NewHandler(svc, logger)
	app := NewApp(cfg, db, handler)
	return app, nil
}
```

### Provider sets reutilizáveis

```go
var InfraSet = wire.NewSet(
	config.Load,
	newLogger,
	postgres.Open,
)

var PedidoSet = wire.NewSet(
	postgres.NewPedidoRepository,
	pedido.NewService,
	httpapi.NewHandler,
)
```

Você compõe sets por domínio e mantém o injector enxuto. Erros de grafo — tipo faltando, ambiguidade de provider, ciclo — aparecem **antes** do deploy, no `go generate` ou no CI.

### O que Wire *não* faz

- Não gerencia `Start`/`Stop` automaticamente.
- Não registra hooks de health check.
- Não resolve “qual implementação de `Repository` usar” por nome mágico — você escolhe com `wire.Bind` ou providers específicos.
- Não substitui [feature flags](/blog/feature-flags-go-rollout-seguro/) nem configuração.

Wire brilha quando o time já escreve bons construtores e só quer **eliminar o boilerplate do `main`** sem pagar o custo de um container.

### Fluxo de trabalho no dia a dia

```bash
go install github.com/google/wire/cmd/wire@latest
# no módulo
wire ./...
go test ./...
```

No CI, falhe se `wire_gen.go` estiver desatualizado (`git diff --exit-code`). Trate o arquivo gerado como artefato versionado — a maioria dos times brasileiros versiona `wire_gen.go` para builds reproduzíveis sem instalar o binário Wire em todo ambiente.

## Uber Fx: aplicação como grafo com lifecycle

[Fx](https://github.com/uber-go/fx) é um **framework de aplicação**. Você registra construtores com `fx.Provide`, invoca funções de bootstrap com `fx.Invoke` e deixa o Fx:

1. montar o grafo;
2. chamar hooks `OnStart` na ordem certa;
3. bloquear até sinal de shutdown;
4. chamar `OnStop` na ordem inversa.

```go
package main

import (
	"context"
	"net/http"

	"go.uber.org/fx"
	"go.uber.org/fx/fxevent"

	"example.com/app/internal/config"
	"example.com/app/internal/httpapi"
	"example.com/app/internal/pedido"
	"example.com/app/internal/postgres"
)

func main() {
	fx.New(
		fx.WithLogger(func() fxevent.Logger { return fxevent.NopLogger }),
		fx.Provide(
			config.Load,
			postgres.Open,
			postgres.NewPedidoRepository,
			pedido.NewService,
			httpapi.NewHandler,
			newHTTPServer,
		),
		fx.Invoke(registerHooks),
	).Run()
}

func newHTTPServer(h *httpapi.Handler, cfg config.Config) *http.Server {
	return &http.Server{
		Addr:    cfg.HTTPAddr,
		Handler: h.Routes(),
	}
}

func registerHooks(lc fx.Lifecycle, srv *http.Server) {
	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)
		},
	})
}
```

### Módulos: o superpoder (e a armadilha)

```go
var Module = fx.Module("pedido",
	fx.Provide(
		postgres.NewPedidoRepository,
		pedido.NewService,
		httpapi.NewHandler,
	),
)
```

Serviços da empresa passam a compartilhar `fx.Module` de logging, tracing, banco e mensageria. Isso acelera novos microsserviços — e também espalha o acoplamento a Fx por dezenas de pacotes se você não isolar o bootstrap.

### Grupos e valores nomeados

Fx permite `fx.Out`, `fx.In`, tags e grupos para coletar várias implementações (vários `http.Handler` de rotas, vários workers). É poderoso em plataformas grandes; em serviços pequenos, vira indirection desnecessária.

### O que Fx resolve melhor que Wire

- **Lifecycle unificado** para HTTP, gRPC, consumers Kafka/SQS e tickers.
- **Ordem de start/stop** sem você manter uma lista manual.
- **Módulos** para times de plataforma entregarem “plugue este pacote e ganhe métricas + health”.
- Integração natural com padrões de [worker pool](/blog/worker-pool-go-fila-jobs/), [health checks](/blog/health-checks-go-liveness-readiness-startup/) e [OpenTelemetry](/blog/go-opentelemetry-observabilidade-tracing-metricas/).

## Comparativo prático: a mesma decisão em três estilos

### 1) Trocar implementação em teste

**Manual / Wire:** no teste, chame `pedido.NewService(fakeRepo, logger)` direto. Não suba o injector de produção.

**Fx:** use `fx.Replace`, `fx.Decorate` ou um `fx.Options` de teste com fakes. Funciona, mas o teste fica mais “framework-aware”.

### 2) Falha de dependência faltando

**Manual:** o compilador reclama do argumento.

**Wire:** `wire` falha com mensagem de provider ausente.

**Fx:** `fx.New` falha em runtime na subida do processo (ainda cedo, mas depois do build).

### 3) Graceful shutdown

**Manual / Wire:** você escreve o loop de sinal — veja o guia de [graceful shutdown](/blog/graceful-shutdown-go-producao/).

**Fx:** `App.Run()` + hooks `OnStop` já modelam o ciclo. Ainda assim, timeouts de shutdown e drenagem de filas são responsabilidade sua.

### 4) Biblioteca reutilizável

Se o pacote é importado por outros times, **não force Wire nem Fx na API pública**. Exporte `NewService(deps)` e deixe o consumidor montar como quiser. O mesmo princípio vale para [middleware HTTP](/blog/middleware-go-net-http-chi-gin-producao/) e clientes de [API REST](/aprenda/api-rest-go/).

## Critérios de decisão para times brasileiros

Use esta rubrica em review de arquitetura:

1. **Tamanho do grafo.** Menos de ~15 construtores no bootstrap? Manual. Entre 15 e 40 com tipos estáveis? Wire. Dezenas de módulos e vários binários? Avalie Fx.
2. **Número de processos no mesmo binário.** Só HTTP? Manual/Wire bastam. HTTP + consumers + cron no mesmo processo? Fx reduz erro de ordem de start.
3. **Maturidade do time.** Wire se lê como Go gerado. Fx exige disciplina de módulos e cuidado para não esconder dependências.
4. **Plataforma interna.** Se já existe um “kit” Fx da empresa, remar contra a maré custa mais do que aprender o framework.
5. **Observabilidade e ops.** Lifecycle central ajuda a padronizar [health](/blog/health-checks-go-liveness-readiness-startup/), métricas e shutdown — desde que o time não use isso para empurrar lógica de negócio para hooks.
6. **Testabilidade.** O domínio continua recebendo interfaces; o framework fica na borda. Se o caso de uso importa Fx, o desenho errou.

## Armadilhas comuns (e como evitar)

### “Container para tudo”

Injetar `*sql.DB`, `*redis.Client`, `*http.Client` e `*slog.Logger` em todo handler cria um god-object distribuído. Prefira interfaces no domínio (`PedidoRepository`, `Clock`, `Publisher`) como no guia de [DI sem framework](/blog/dependency-injection-go-sem-framework/).

### Esconder configuração no grafo

Configuração deve ser validada cedo — idealmente com o padrão [Twelve-Factor + Viper/env](/blog/configuracao-go-viper-variaveis-ambiente/). Não deixe um provider ler `os.Getenv` no meio do grafo sem falhar rápido.

### Wire com providers ambíguos

Dois `NewRepository` que devolvem a mesma interface quebram o grafo. Use `wire.Bind` explícito ou tipos distintos por bounded context.

### Fx Invoke com side effects opacos

`fx.Invoke` que “só registra rotas” e também abre conexão global vira caça ao tesouro. Hooks de lifecycle devem ser óbvios: abrir listener, iniciar consumer, fechar pool.

### Gerar código e não rodar no CI

`wire_gen.go` desatualizado é bug de produção esperando o merge. Trate `wire ./...` como passo de lint, no mesmo espírito de [staticcheck/golangci-lint](/blog/staticcheck-golangci-lint-go-qualidade-ci/).

### Misturar DI framework com service locator

Passar o `fx.App` ou um `container.Get("repo")` para o domínio anula o benefício. Dependências entram pelo construtor — sempre.

## Como migrar sem reescrever o monólito

### Do manual para Wire

1. Extraia cada `New*` para assinaturas puras (sem globals).
2. Crie `provider sets` por pacote.
3. Substitua o `main` por um injector; versionar `wire_gen.go`.
4. Mantenha testes unitários chamando construtores diretamente — não o injector.

### Do manual para Fx

1. Isole o bootstrap em `internal/app` ou `cmd/api`.
2. Mova `Provide` sem mudar o domínio.
3. Introduza `Lifecycle` só onde há start/stop real (HTTP, consumers).
4. Só depois quebre em `fx.Module` por bounded context.

### De Fx para Wire (ou vice-versa)

Raro e caro. Só faça se o framework virou atrito mensurável (builds lentos, onboarding, incidentes de ordem de start). Migre um binário por vez; não “padronize a empresa” em um PR heróico.

## O que isso tem a ver com vagas e entrevistas Go

Descrições de vagas backend plenas/sênior no Brasil pedem cada vez mais “experiência com DI”, “modularização” e “produção”. Em entrevista, o sinal de maturidade não é citar Wire ou Fx de cor — é explicar **quando o main manual deixa de escalar**, como testar com fakes e como o lifecycle evita vazamento de conexões no deploy. Combine isso com repertório de [context e timeout](/blog/context-timeout-cancelamento-go/), [rate limiting](/blog/rate-limiting-go-api-producao/) e [observabilidade](/blog/go-opentelemetry-observabilidade-tracing-metricas/).

Se você está montando portfólio, um serviço pequeno com construtores manuais bem testados vale mais do que um CRUD com Fx copiado de template. Veja ideias em [projetos Go para portfólio](/blog/projetos-go-para-portfolio-ideias-primeira-vaga/) e o [roadmap Go 2026](/aprenda/roadmap-go-2026/).

## Checklist antes de adotar Wire ou Fx

- [ ] Interfaces do domínio são pequenas e estão perto de quem usa?
- [ ] O `main` atual é realmente o gargalo (não a modelagem)?
- [ ] Configuração falha cedo e de forma explícita?
- [ ] Testes unitários não precisam do container?
- [ ] CI valida código gerado (Wire) ou sobe o app com smoke test (Fx)?
- [ ] Shutdown drena requests e workers com timeout?
- [ ] Logs estruturados e correlação existem independentemente do DI?
- [ ] A escolha está documentada no README do serviço (uma página, sem dogma)?

Se a maioria das respostas for “não”, volte ao [guia sem framework](/blog/dependency-injection-go-sem-framework/) e ao [clean architecture sem overengineering](/blog/clean-architecture-go-sem-overengineering/).

## Perguntas frequentes

### Qual a diferença entre Google Wire e Uber Fx?

Wire gera código Go em tempo de compilação e monta o grafo com funções puras, sem container em runtime. Fx monta o grafo em runtime, com módulos, lifecycle (`OnStart`/`OnStop`), grupos de dependências e hooks de inicialização. Wire é mais próximo de construtores manuais; Fx é um framework de aplicação.

### Quando usar Wire em vez de Fx?

Use Wire quando o grafo cresceu demais para o `main` manual, mas você quer manter DI explícita, zero magia em runtime e falhas detectadas no build. É a evolução natural do padrão de construtores.

### Quando Uber Fx vale a pena?

Fx vale a pena em serviços grandes com muitos módulos, inicialização ordenada, graceful shutdown centralizado e times que compartilham pacotes de infraestrutura (logger, métricas, banco, filas). O custo é acoplamento ao framework e uma curva de aprendizado maior.

### Preciso de Wire ou Fx para fazer DI em Go?

Não. A forma idiomática começa com structs, interfaces pequenas e construtores no `main`. Ferramentas de DI só entram quando a montagem manual virou fonte real de erro ou o lifecycle da aplicação ficou complexo demais para um bootstrap legível.

### Wire e Fx podem coexistir no mesmo projeto?

Tecnicamente sim, mas raramente vale a pena. Escolha um modelo de montagem por serviço. Misturar gera dois estilos de bootstrap, dois jeitos de testar e onboarding mais caro. Prefira um padrão único por repositório.

## Conclusão

**Google Wire** e **Uber Fx** resolvem dores reais — mas só depois que o time já domina DI idiomática com construtores. Wire é a ponte conservadora: menos boilerplate, mesmo modelo mental, erros no build. Fx é a aposta de plataforma: módulos, lifecycle e composição em escala, com mais indirection. Na dúvida, mantenha o `main` explícito, invista em interfaces testáveis e só introduza ferramenta quando o grafo ou o ciclo de vida cobrarem o preço.

Continue com [dependency injection sem framework](/blog/dependency-injection-go-sem-framework/), [Clean Architecture em Go](/blog/clean-architecture-go-sem-overengineering/), [mocks e fakes](/blog/mocks-go-testify-gomock-fakes-httptest/), [graceful shutdown](/blog/graceful-shutdown-go-producao/) e as [vagas Go no Brasil](/vagas/). Se ainda está estruturando a base da linguagem, o caminho [como aprender Go](/aprenda/como-aprender-go/) e o [curso gratuito](/aprenda/curso-golang-gratuito/) fecham o funil de aprendizado.
