¿Qué es un monorepositorio y por qué es necesario en Go?
Un monorepositorio (monorepo) es un enfoque para la gestión de código donde todos los proyectos, bibliotecas y servicios se almacenan en un solo repositorio. En el ecosistema de Go, este enfoque se vuelve especialmente popular gracias al soporte integrado de módulos y workspaces.
Para los desarrolladores principiantes, un monorepositorio ofrece varias ventajas clave:
- Versión única de código — todas las dependencias están sincronizadas
- Refactorización simplificada — los cambios son visibles de inmediato en todos los proyectos
- Estandarización — configuraciones idénticas de linters, CI/CD y pruebas
- Transparencia — es fácil entender cómo se relacionan las diferentes partes del sistema
En este artículo, analizaremos cómo organizar correctamente un monorepositorio en Go, comenzando con la estructura básica y terminando con ejemplos prácticos.
Estructura básica de un monorepositorio en Go
La organización adecuada del código es la base de un monorepositorio exitoso. Consideremos una estructura típica:
my-monorepo/├── apps/│ ├── web-api/ # Aplicación principal│ │ ├── main.go│ │ ├── go.mod│ │ └── internal/│ └── worker/ # Procesador en segundo plano│ ├── main.go│ └── go.mod├── libs/│ ├── config/ # Configuración general│ │ ├── config.go│ │ └── go.mod│ └── logger/ # Registro (logging)│ ├── logger.go│ └── go.mod├── scripts/ # Scripts auxiliares├── docs/ # Documentación├── go.work # Go workspace└── README.mdPrincipios clave de esta estructura:
- apps/ — aplicaciones ejecutables (binarios)
- libs/ — bibliotecas reutilizables
- scripts/ — automatización y utilidades
- docs/ — documentación del proyecto
Configuración de Go Workspace para el monorepositorio
A partir de Go 1.18, se introdujo el soporte oficial de workspaces a través del archivo go.work. Esto permite trabajar con varios módulos simultáneamente sin necesidad de publicarlos en un repositorio remoto.
Creemos un workspace para nuestro monorepositorio:
// go.workgo 1.22
use ( ./apps/web-api ./apps/worker ./libs/config ./libs/logger)Después de crear go.work, todos los módulos de la lista use estarán disponibles entre sí localmente. Esto es especialmente conveniente durante el desarrollo: los cambios en una biblioteca se ven inmediatamente en las aplicaciones.
Inicialización de módulos
Para cada componente, es necesario crear su propio go.mod. Ejemplo para la biblioteca de configuración:
// libs/config/go.modmodule github.com/your-org/libs/config
go 1.22Y para la aplicación web-api:
// apps/web-api/go.modmodule github.com/your-org/apps/web-api
go 1.22
require github.com/your-org/libs/config v0.0.0
replace github.com/your-org/libs/config => ../../libs/configObserve el replace — le indica a Go que use la versión local de la biblioteca, en lugar de buscarla en internet.
Ejemplo de código: biblioteca y aplicación
Creemos una biblioteca simple para configuración y una aplicación que la utilice.
Biblioteca libs/config
// libs/config/config.gopackage config
import ( "fmt" "os")
type Config struct { Port string Env string}
func Load() (*Config, error) { port := os.Getenv("PORT") if port == "" { port = "8080" }
env := os.Getenv("ENV") if env == "" { env = "development" }
return &Config{ Port: port, Env: env, }, nil}
func (c *Config) String() string { return fmt.Sprintf("Config{