Monorepositorio con Go: organización de código en la práctica
\n\nUn monorepositorio (monorepo) es un enfoque para la gestión del código fuente donde todos los proyectos, bibliotecas y servicios se almacenan en un solo repositorio. En el mundo de Go, este enfoque se ha vuelto especialmente popular gracias al soporte integrado de módulos y al sistema flexible de paquetes. En este artículo, analizaremos cómo organizar un monorepositorio en Go en la práctica, revisaremos una estructura típica, ejemplos de código y las mejores prácticas.
\n\n¿Por qué es necesario un monorepositorio en Go?
\n\nEl monorepositorio resuelve varias tareas clave:
\n- \n
- Simplificación de la gestión de dependencias — todos los paquetes internos están en un solo lugar, lo que facilita su actualización y refactorización. \n
- Historial de cambios unificado — los commits atómicos que afectan a varios servicios a la vez funcionan sin sincronizaciones adicionales. \n
- Aceleración de CI/CD — se pueden configurar pipelines que compilen solo las partes modificadas del proyecto. \n
- Mejora de la calidad del código — es más fácil implementar estándares uniformes, linters y code reviews. \n
Estructura del monorepositorio Go: esquema básico
\n\nConsideremos una estructura típica de un monorepositorio en Go. Como ejemplo, tomemos un proyecto que consta de dos microservicios (api y worker) y una biblioteca compartida lib:
my-monorepo/\n├── go.work\n├── go.work.sum\n├── apps/\n│ ├── api/\n│ │ ├── go.mod\n│ │ ├── main.go\n│ │ └── handler.go\n│ └── worker/\n│ ├── go.mod\n│ ├── main.go\n│ └── process.go\n├── lib/\n│ ├── go.mod\n│ ├── auth/\n│ │ └── auth.go\n│ └── db/\n│ └── db.go\n├── scripts/\n│ ├── build.sh\n│ └── test.sh\n└── Makefile\n\nElementos clave:
\n- \n
go.work— archivo del área de trabajo de Go (Workspace) que vincula todos los módulos. \n apps/— directorio con las aplicaciones (microservicios, utilidades CLI, etc.). \n lib/— bibliotecas compartidas utilizadas por todas las aplicaciones. \n scripts/— scripts auxiliares para compilación, pruebas y despliegue. \n
Configuración de Go Workspace (go.work)
\n\nGo 1.18+ proporciona el mecanismo workspace, que permite trabajar con varios módulos simultáneamente. Creemos el archivo go.work en la raíz:
go 1.22\n\nuse (\n ./apps/api\n ./apps/worker\n ./lib\n)\n\nAhora, durante el desarrollo, se pueden ejecutar comandos desde la raíz del proyecto, y Go verá todos los módulos como un todo. Por ejemplo, para ejecutar las pruebas de todos los módulos:
\n\ngo test ./...\n\nY para compilar un servicio específico:
\n\ncd apps/api && go build -o bin/api .\n\nEjemplo de código: biblioteca compartida y microservicio
\n\nCreemos una biblioteca simple para autenticación en lib/auth/auth.go:
package auth\n\nimport "errors"\n\nvar (\n ErrInvalidToken = errors.New("invalid token")\n)\n\n// ValidateToken verifica el token y devuelve el ID del usuario.\nfunc ValidateToken(token string) (string, error) {\n if token == "" {\n return "", ErrInvalidToken\n }\n // Aquí podría haber una verificación real de JWT o una llamada a un servicio externo\n return "user-123", nil\n}\n\nAhora usemos esta biblioteca en el microservicio apps/api/handler.go:
package main\n\nimport (\n "encoding/json"\n "net/http"\n "my-monorepo/lib/auth"\n)\n\ntype UserResponse struct {\n UserID string `json:"user_id"`\n}\n\nfunc UserHandler(w http.ResponseWriter, r *http.Request) {\n token := r.Header.Get("Authorization")\n userID, err := auth.ValidateToken(token)\n if err != nil {\n http.Error(w, "Unauthorized", http.StatusUnauthorized)\n return\n }\n\n resp := UserResponse{UserID: user