Injeção de Dependência (Dependency Injection) em Go: 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

Introdução: Por que o Dependency Injection é importante em Go?



Go (Golang) é uma linguagem que valoriza simplicidade e clareza. No entanto, à medida que o projeto cresce, surge a necessidade de gerenciar dependências entre componentes. Dependency Injection (DI) ou injeção de dependências é um padrão de design que ajuda a criar código fracamente acoplado, testável e facilmente mantível. Diferente de frameworks com magia de reflexão (como Spring em Java), Go oferece uma abordagem mais pragmática: passar dependências através de interfaces e construtores.



Neste artigo, vamos analisar como implementar DI em Go sem bibliotecas de terceiros, usando interfaces puras, e também considerar contêineres DI populares. Você aprenderá como a injeção de dependências ajuda a escrever testes unitários e seguir os princípios SOLID.



Fundamentos: Interfaces e injeção via construtor



Em Go não há classes, mas existem interfaces — que são contratos de comportamento. Elas são a base para DI. Em vez de criar uma implementação concreta dentro da struct, nós a passamos de fora.



Exemplo sem DI (acoplamento forte)



package main

import "fmt"

// Logger — implementação concretatype Logger struct{}

func (l *Logger) Log(msg string) { fmt.Println("LOG:", msg)}

// Service — depende fortemente de Loggertype Service struct { logger *Logger}

func NewService() *Service { return &Service{logger: &Logger{}}}

func (s *Service) DoWork() { s.logger.Log("Trabalho concluído")}

func main() { svc := NewService() svc.DoWork()}


Problema: Service não pode ser testado sem criar um Logger real. Substituí-lo por um mock ou outro logger é impossível.



Exemplo com DI via interface



package main

import "fmt"

// LogInterface — abstração para loggingtype LogInterface interface { Log(msg string)}

// ConsoleLogger — implementaçãotype ConsoleLogger struct{}

func (l *ConsoleLogger) Log(msg string) { fmt.Println("LOG:", msg)}

// Service — agora depende da interfacetype Service struct { logger LogInterface}

// NewService — construtor com injeção de dependênciafunc NewService(logger LogInterface) *Service { return &Service{logger: logger}}

func (s *Service) DoWork() { s.logger.Log("Trabalho concluído")}

func main() { logger := &ConsoleLogger{} svc := NewService(logger) svc.DoWork()}


Agora Service funciona com qualquer objeto que implemente LogInterface. Isso simplifica os testes: podemos passar um mock (stub).



Técnica avançada: Contêineres DI em Go



Quando o projeto cresce, passar dependências manualmente se torna tedioso. É aí que entram os contêineres DI — bibliotecas que resolvem automaticamente o grafo de dependências. Em Go, são populares google/wire (do Google) e uber-go/dig (da Uber).



Exemplo com google/wire



Wire usa code generation, o que elimina a reflexão em tempo de execução — isso está no espírito do Go.



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

package main

import "github.com/google/wire"

func InitializeService() *Service { wire.Build(NewService, NewConsoleLogger) return nil}


Após executar wire, ele gerará código que criará um ConsoleLogger e o passará para NewService.



Exemplo com uber-go/dig



Dig funciona através de reflexão, mas é muito conveniente para montagem dinâmica.



package main

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

func main() { container := dig.New()

// Registramos os construtores container.Provide(NewConsoleLogger) container.Provide(NewService)

// Chamamos a função que usa a dependên

Blogs

Book Recommendations