Monorepository mit Go: Code-Organisation für skalierbare Projekte
In der modernen Softwareentwicklung stellt sich immer häufiger die Frage: Wie organisiert man Code, sodass er verwaltbar, wiederverwendbar und skalierbar bleibt? Eine beliebte Lösung ist die Verwendung eines Monorepositorys (Monorepo) – eines einzigen Repositorys, das mehrere Projekte oder Dienste enthält. In diesem Artikel werden wir untersuchen, wie man ein Monorepository in Go (Golang) effektiv organisiert, Best Practices, die Verzeichnisstruktur, die Arbeit mit Modulen und die Einrichtung von CI/CD betrachten.
Was ist ein Monorepository und warum wird es in Go benötigt?
Ein Monorepository ist ein Ansatz, bei dem der gesamte Code eines Unternehmens oder eines großen Projekts in einem einzigen Repository gespeichert wird. Dies kann Folgendes umfassen:
- Mehrere Microservices
- Bibliotheken und Hilfsprogramme
- Konfigurationen und Skripte
- Dokumentation
Vorteile eines Monorepositorys für Go:
- Einheitliche Abhängigkeitsversion – vermeidet Versionskonflikte zwischen Diensten.
- Vereinfachtes Refactoring – Änderungen an einer gemeinsamen Bibliothek sind sofort für alle Nutzer sichtbar.
- Einfache Wiederverwendung von Code – Pakete müssen nicht in einer Registry veröffentlicht werden.
- Einheitliche Commit-Historie – vereinfacht Debugging und Audits.
Ein Monorepository erfordert jedoch Disziplin: Ohne eine klare Struktur wird es schnell zu einer "Müllhalde". Go mit seinem strengen Modulsystem (Go Modules) bietet hervorragende Werkzeuge zur Organisation eines solchen Repositorys.
Verzeichnisstruktur in einem Go-Monorepository
Die richtige Struktur ist die Grundlage für den Erfolg. Betrachten wir eine empfohlene Organisation für ein Monorepository in Go:
my-monorepo/├── apps/ # Anwendungen (Dienste, CLI-Tools)│ ├── api-gateway/│ │ ├── cmd/│ │ │ └── main.go│ │ ├── internal/│ │ ├── go.mod│ │ └── go.sum│ ├── auth-service/│ │ └── ...│ └── user-service/│ └── ...├── libs/ # Gemeinsame Bibliotheken│ ├── common/│ │ ├── logger.go│ │ ├── config.go│ │ ├── go.mod│ │ └── go.sum│ └── models/│ ├── user.go│ ├── go.mod│ └── go.sum├── scripts/ # Build- und Deployment-Skripte│ ├── build.sh│ └── deploy.sh├── docs/ # Dokumentation├── .github/ # CI/CD-Konfigurationen│ └── workflows/│ └── ci.yml├── go.work # Workspace-Datei (Go 1.18+)├── Makefile # Allgemeine Befehle├── README.md└── .gitignoreWichtige Elemente der Struktur:
- apps/ – enthält ausführbare Anwendungen (Dienste, Tools). Jede Anwendung ist ein separates Go-Modul mit eigenem
go.mod. - libs/ – gemeinsame Bibliotheken, die zwischen den Anwendungen wiederverwendet werden.
- go.work – Workspace-Datei, die es ermöglicht, lokal mit allen Modulen zu arbeiten, ohne Änderungen veröffentlichen zu müssen.
Arbeiten mit Go Modules in einem Monorepository
Seit Go 1.18 gibt es Unterstützung für Workspaces (go.work). Dies löst das Problem der lokalen Entwicklung, wenn Module voneinander abhängen. Betrachten wir ein Beispiel für die Einrichtung.
Erstellen wir eine Datei go.work im Stammverzeichnis des Monorepositorys:
go 1.21
use ( ./apps/api-gateway ./apps/auth-service ./libs/common ./libs/models)Jetzt verwendet Go bei der lokalen Entwicklung die lokalen Versionen der Module und nicht die veröffentlichten. Dies ist für schnelle Iterationen von entscheidender Bedeutung.
Beispiel für eine Abhängigkeit in apps/auth-service/go.mod:
module github.com/company/auth-service
go 1.21
require ( github.com/company/libs/common v0.0.0 github.com/company/libs/model