Injeção de Dependência em Go: princípios, exemplos e melhores práticas

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: princípios, exemplos e melhores práticas



Injeção de Dependência (DI) é um dos princípios-chave de design que ajuda a criar aplicações flexíveis, testáveis e fáceis de manter. Na linguagem Go, apesar de sua simplicidade e ausência de contêineres DI embutidos, a injeção de dependências é amplamente utilizada. Neste artigo, vamos analisar o que é Injeção de Dependência em Go, como implementá-la manualmente e com bibliotecas populares, além de considerar as melhores práticas.



O que é Injeção de Dependência?

Injeção de Dependência é um padrão de design no qual um objeto recebe suas dependências externamente, em vez de criá-las por conta própria. O objetivo principal é reduzir o acoplamento entre componentes e aumentar a testabilidade do código.



Em Go, a DI é implementada através da passagem de dependências para estruturas (structs) ou funções. Vamos considerar um exemplo simples sem DI:



package main

import "fmt"

type Logger struct{}

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

type Service struct { logger *Logger}

func NewService() *Service { return &Service{ logger: &Logger{}, // Acoplamento rígido }}

func (s *Service) DoSomething() { s.logger.Log("Doing something")}


E agora o mesmo exemplo usando DI:



package main

import "fmt"

type Logger interface { Log(message string)}

type ConsoleLogger struct{}

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

type Service struct { logger Logger // Dependência através de interface}

func NewService(logger Logger) *Service { return &Service{logger: logger} // Injeção de dependência}

func (s *Service) DoSomething() { s.logger.Log("Doing something")}


Vantagens da Injeção de Dependência em Go

  • Testabilidade: Você pode facilmente substituir dependências reais por mocks ou stubs nos testes.
  • Flexibilidade: Alterar o comportamento da aplicação se resume a substituir a implementação da dependência.
  • Limpeza do código: O código se torna mais modular, cada estrutura é responsável apenas por sua lógica.
  • Simplificação de refatoração: As dependências são explicitamente declaradas, facilitando a compreensão e alteração do código.


Formas de injeção de dependências em Go

Em Go, existem várias abordagens para implementar DI. Vamos considerar as principais.



1. Injeção via construtor (Constructor Injection)

A forma mais comum. As dependências são passadas através de uma função construtora (NewSomething()).



type UserRepository interface {    FindByID(id int) (*User, error)}

type UserService struct { repo UserRepository}

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


2. Injeção via campos da estrutura (Field Injection)

As dependências são atribuídas diretamente aos campos da estrutura. Esta abordagem é menos preferível, pois pode levar à inicialização incompleta do objeto.



type UserService struct {    Repo UserRepository}

func main() { svc := &UserService{} svc.Repo = &PostgresRepository{}}


3. Injeção via métodos (Method Injection)

A dependência é passada para o método, e não para o construtor. Útil para dependências temporárias.



type UserService struct{}

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


Exemplo: construção de um servidor HTTP com DI

Vamos considerar um exemplo mais complexo — a criação de um servidor HTTP com injeção de dependências.



package main

import ( "encoding/json" "net/http")

// Definimos as interfacestype UserRepository int

Blogs

Book Recommendations