Injeção de Dependência em Go: um guia prático

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Injeção de Dependência em Go: da teoria ao código real



Injeção de Dependência (DI) é um dos padrões de design fundamentais que ajuda a criar código fracamente acoplado, testável e de fácil manutenção. Em Go, com sua filosofia de simplicidade e clareza, a injeção de dependências pode ser implementada tanto manualmente quanto com o uso de bibliotecas. Neste artigo, vamos explorar como aplicar DI em Golang na prática, abordando a abordagem manual e também a popular biblioteca google/wire.



1. Por que a Injeção de Dependência é necessária em Go?



Imagine que você está escrevendo um serviço HTTP para trabalhar com usuários. Sem DI, seu código pode se parecer com isto:



package main

import "database/sql"

type UserHandler struct { db *sql.DB}

func NewUserHandler() *UserHandler { db, _ := sql.Open("postgres", "conn_string") return &UserHandler{db: db}}

func (h *UserHandler) GetUser(id int) (*User, error) { // trabalhando com h.db}


Os problemas são óbvios:

  • Acoplamento rígido — o handler cria o banco de dados por conta própria.
  • Dificuldade de teste — é impossível substituir db por um mock.
  • Violação do Princípio da Responsabilidade Única — o handler é responsável tanto pela criação das dependências quanto pela lógica de negócios.


DI resolve esses problemas passando as dependências de fora:



type UserHandler struct {    db Database}

func NewUserHandler(db Database) *UserHandler { return &UserHandler{db: db}}

// Agora é fácil substituir por um mocktype Database interface { GetUser(id int) (*User, error)}


2. Injeção manual de dependências: simplicidade e controle



Go não requer frameworks para DI. A maneira mais simples e transparente é passar dependências através de construtores. Vamos considerar um exemplo com três camadas da aplicação: handler → service → repository.



// repository/user.gotype UserRepository interface {    FindByID(id int) (*User, error)}

type postgresUserRepo struct { db *sql.DB}

func NewPostgresUserRepo(db *sql.DB) *postgresUserRepo { return &postgresUserRepo{db: db}}

func (r *postgresUserRepo) FindByID(id int) (*User, error) { // implementação}


// service/user.gotype UserService struct {    repo UserRepository}

func NewUserService(repo UserRepository) *UserService { return &UserService{repo: repo}}

func (s *UserService) GetUser(id int) (*User, error) { return s.repo.FindByID(id)}


// handler/user.gotype UserHandler struct {    svc *UserService}

func NewUserHandler(svc *UserService) *UserHandler { return &UserHandler{svc: svc}}

func (h *UserHandler) HandleGetUser(w http.ResponseWriter, r *http.Request) { // trabalhando com h.svc}


Agora a montagem da aplicação ocorre em main.go:



func main() {    db, _ := sql.Open("postgres", os.Getenv("DB_URL"))        repo := NewPostgresUserRepo(db)    svc := NewUserService(repo)    handler := NewUserHandler(svc)        http.HandleFunc("/user", handler.HandleGetUser)    log.Fatal(http.ListenAndServe(":8080", nil))}


Vantagens do DI manual:

  • Transparência total — você vê todas as dependências explicitamente.
  • Sem magia e dependências desnecessárias no projeto.
  • Ideal para projetos pequenos e médios.


Desvantagens: à medida que a aplicação cresce (50+ componentes), a montagem manual se torna complicada e propensa a erros.



3. Automação com google/wire



A biblioteca github.com/google/wire gera código para montagem de dependências. Ela não é um container em tempo de execução — é um gerador de código, o que está totalmente alinhado com a filosofia do Go.



Instalação:

go get github.com/google/wir                

Blogs

Book Recommendations