Contenedor de Inyección de Dependencias para Go (Golang): Wire, Dig y Uber FX
La Inyección de Dependencias (DI) es un patrón de diseño en el cual un objeto recibe sus dependencias desde el exterior, en lugar de crearlas él mismo. En Go (Golang), la DI es especialmente útil para crear código modular, testeable y escalable. Las herramientas DI más populares para Go son Google Wire, Uber Dig y Uber FX. En este artículo, analizaremos cada una de ellas, sus características y ejemplos de uso.
¿Por qué es necesaria la Inyección de Dependencias en Go?
Go es un lenguaje de tipado estático con paso explícito de parámetros. Sin DI, los desarrolladores suelen crear variables globales o fábricas, lo que complica las pruebas y el mantenimiento. Los contenedores DI resuelven estos problemas:
- Bajo acoplamiento — los componentes dependen de interfaces, no de implementaciones concretas.
- Testeabilidad — es fácil reemplazar dependencias con mocks.
- Simplificación de la configuración — el contenedor resuelve automáticamente el grafo de dependencias.
Google Wire — generación de código en tiempo de compilación
Wire de Google no es un contenedor en tiempo de ejecución, sino un generador de código. Analiza las funciones constructoras y genera código para vincular dependencias en la etapa de compilación. Esto proporciona una carga nula durante la ejecución y una detección temprana de errores.
Instalación de Wire
go install github.com/google/wire/cmd/wire@latestEjemplo de uso de Wire
Supongamos que tenemos un servicio que depende de una base de datos y un registrador (logger):
// main.gopackage main
import "fmt"
type Logger struct{}
func NewLogger() Logger { return Logger{} }
func (l Logger) Log(msg string) { fmt.Println(msg) }
type Database struct { logger Logger}
func NewDatabase(logger Logger) Database { return Database{logger: logger}}
type Service struct { db Database log Logger}
func NewService(db Database, log Logger) Service { return Service{db: db, log: log}}Ahora creemos un archivo wire.go con la instrucción para Wire:
// wire.go//go:build wireinject// +build wireinject
package main
import "github.com/google/wire"
func InitializeService() Service { wire.Build(NewLogger, NewDatabase, NewService) return Service{}}Ejecutemos la generación:
wireWire creará un archivo wire_gen.go con el código de inicialización listo:
// wire_gen.go// Code generated by Wire. DO NOT EDIT.
package main
func InitializeService() Service { logger := NewLogger() database := NewDatabase(logger) service := NewService(database, logger) return service}Ventajas de Wire:
- Errores en tiempo de compilación.
- Sin reflection, alto rendimiento.
- Código simple y predecible.
Desventajas de Wire:
- Requiere un paso de generación separado.
- Difícil de usar con dependencias dinámicas.
Uber Dig — contenedor en tiempo de ejecución con reflection
Dig de Uber es un contenedor que funciona en tiempo de ejecución. Utiliza reflection para analizar tipos y resolver dependencias automáticamente. Dig proporciona una API simple para registrar constructores y extraer objetos.
Instalación de Dig
go get go.uber.org/digEjemplo de uso de Dig
package main
import ( "fmt" "go.uber.org/dig")
type Config struct { Port int}
func NewConfig() *Config { return &Config{Port: 8080}}
type Server struct { config *Config}
func NewServer(c *Config) *Server { return &Server{config: c}}
func (s *Server) Start() { fmt.Print