Monorepo avec Go : organisation du code en pratique
\n\nUn monorepo est une approche de gestion du code source où tous les projets, bibliothèques et services sont stockés dans un seul dépôt. Dans le monde de Go, cette approche est devenue particulièrement populaire grâce à la prise en charge intégrée des modules et au système flexible de paquets. Dans cet article, nous allons voir comment organiser un monorepo en Go en pratique, examiner une structure typique, des exemples de code et les meilleures pratiques.
\n\nPourquoi un monorepo en Go ?
\n\nUn monorepo résout plusieurs problèmes clés :
\n- \n
- Simplification de la gestion des dépendances — tous les paquets internes se trouvent au même endroit, faciles à mettre à jour et à refactoriser. \n
- Historique unique des modifications — des commits atomiques qui affectent plusieurs services à la fois fonctionnent sans synchronisations inutiles. \n
- Accélération du CI/CD — possibilité de configurer des pipelines qui ne construisent que les parties modifiées du projet. \n
- Amélioration de la qualité du code — plus facile d’imposer des normes communes, des linters et des revues de code. \n
Structure d’un monorepo Go : schéma de base
\n\nExaminons une structure typique d’un monorepo en Go. Prenons comme exemple un projet composé de deux microservices (api et worker) et d’une bibliothèque commune 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\nÉléments clés :
\n- \n
go.work— fichier d’espace de travail Go (Workspace) qui relie tous les modules. \n apps/— répertoire contenant les applications (microservices, utilitaires CLI, etc.). \n lib/— bibliothèques communes utilisées par toutes les applications. \n scripts/— scripts auxiliaires pour la construction, les tests et le déploiement. \n
Configuration de Go Workspace (go.work)
\n\nGo 1.18+ fournit le mécanisme workspace, qui permet de travailler avec plusieurs modules simultanément. Créons un fichier go.work à la racine :
go 1.22\n\nuse (\n ./apps/api\n ./apps/worker\n ./lib\n)\n\nDésormais, lors du développement, vous pouvez exécuter des commandes depuis la racine du projet, et Go verra tous les modules comme un tout. Par exemple, pour lancer les tests de tous les modules :
\n\ngo test ./...\n\nEt pour construire un service spécifique :
\n\ncd apps/api && go build -o bin/api .\n\nExemple de code : bibliothèque commune et microservice
\n\nCréons une bibliothèque simple pour l’authentification dans lib/auth/auth.go :
package auth\n\nimport "errors"\n\nvar (\n ErrInvalidToken = errors.New("invalid token")\n)\n\n// ValidateToken vérifie le token et retourne l’ID de l’utilisateur.\nfunc ValidateToken(token string) (string, error) {\n if token == "" {\n return "", ErrInvalidToken\n }\n // Ici pourrait se trouver une véritable vérification JWT ou un appel à un service externe\n return "user-123", nil\n}\n\nUtilisons maintenant cette bibliothèque dans le microservice 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