Monorepository mit Go: Code-Organisation in der Praxis

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Monorepository mit Go: Code-Organisation in der Praxis

\n\n

Ein Monorepository (Monorepo) ist ein Ansatz zur Verwaltung von Quellcode, bei dem alle Projekte, Bibliotheken und Dienste in einem einzigen Repository gespeichert werden. In der Go-Welt ist dieser Ansatz dank der integrierten Modulunterstützung und des flexiblen Paketsystems besonders beliebt geworden. In diesem Artikel werden wir uns ansehen, wie man ein Monorepository in Go in der Praxis organisiert, eine typische Struktur, Codebeispiele und Best Practices betrachten.

\n\n

Warum ein Monorepository in Go?

\n\n

Ein Monorepository löst mehrere zentrale Aufgaben:

\n
    \n
  • Vereinfachung der Abhängigkeitsverwaltung — alle internen Pakete befinden sich an einem Ort, sie lassen sich leicht aktualisieren und refaktorieren.
  • \n
  • Einheitliche Änderungshistorie — atomare Commits, die mehrere Dienste gleichzeitig betreffen, funktionieren ohne zusätzliche Synchronisation.
  • \n
  • Schnellere CI/CD — Pipelines können so konfiguriert werden, dass sie nur die geänderten Teile des Projekts erstellen.
  • \n
  • Höhere Codequalität — einheitliche Standards, Linter und Code-Reviews lassen sich leichter durchsetzen.
  • \n
\n\n

Struktur eines Go-Monorepositorys: Grundlegendes Schema

\n\n

Betrachten wir eine typische Struktur eines Go-Monorepositorys. Als Beispiel nehmen wir ein Projekt, das aus zwei Mikrodiensten (api und worker) und einer gemeinsamen Bibliothek lib besteht:

\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

Schlüsselelemente:

\n
    \n
  • go.work — die Go-Workspace-Datei, die alle Module miteinander verbindet.
  • \n
  • apps/ — Verzeichnis mit Anwendungen (Mikrodienste, CLI-Tools usw.).
  • \n
  • lib/ — gemeinsame Bibliotheken, die von allen Anwendungen genutzt werden.
  • \n
  • scripts/ — Hilfsskripte für Build, Tests und Deployment.
  • \n
\n\n

Einrichtung des Go Workspace (go.work)

\n\n

Go 1.18+ bietet den Mechanismus workspace, der es ermöglicht, mit mehreren Modulen gleichzeitig zu arbeiten. Erstellen wir die Datei go.work im Stammverzeichnis:

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

Jetzt können während der Entwicklung Befehle aus dem Stammverzeichnis des Projekts ausgeführt werden, und Go sieht alle Module als eine Einheit. Um beispielsweise Tests für alle Module auszuführen:

\n\n
go test ./...
\n\n

Und um einen bestimmten Dienst zu erstellen:

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

Codebeispiel: Gemeinsame Bibliothek und Mikrodienst

\n\n

Erstellen wir eine einfache Authentifizierungsbibliothek in lib/auth/auth.go:

\n\n
package auth\n\nimport "errors"\n\nvar (\n    ErrInvalidToken = errors.New("invalid token")\n)\n\n// ValidateToken prüft das Token und gibt die Benutzer-ID zurück.\nfunc ValidateToken(token string) (string, error) {\n    if token == "" {\n        return "", ErrInvalidToken\n    }\n    // Hier könnte eine echte JWT-Überprüfung oder ein Aufruf eines externen Dienstes erfolgen\n    return "user-123", nil\n}
\n\n

Jetzt verwenden wir diese Bibliothek im Mikrodienst 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

Buchempfehlungen