Monorepositorio con Go: organización de código en la práctica

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Monorepositorio con Go: organización de código en la práctica

\n\n

Un 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\n

El 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
\n\n

Estructura del monorepositorio Go: esquema básico

\n\n

Consideremos 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:

\n\n
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\n

Elementos 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
\n\n

Configuración de Go Workspace (go.work)

\n\n

Go 1.18+ proporciona el mecanismo workspace, que permite trabajar con varios módulos simultáneamente. Creemos el archivo go.work en la raíz:

\n\n
go 1.22\n\nuse (\n    ./apps/api\n    ./apps/worker\n    ./lib\n)
\n\n

Ahora, 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\n
go test ./...
\n\n

Y para compilar un servicio específico:

\n\n
cd apps/api && go build -o bin/api .
\n\n

Ejemplo de código: biblioteca compartida y microservicio

\n\n

Creemos una biblioteca simple para autenticación en lib/auth/auth.go:

\n\n
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\n

Ahora usemos esta biblioteca en el microservicio apps/api/handler.go:

\n\n
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                

Blogs

Book Recommendations