Was ist ein Monorepository und warum wird es in Go benötigt?
Ein Monorepository (Monorepo) ist ein Ansatz zur Codeverwaltung, bei dem alle Projekte, Bibliotheken und Dienste in einem einzigen Repository gespeichert werden. Im Go-Ökosystem wird dieser Ansatz aufgrund der integrierten Unterstützung für Module und Workspaces besonders beliebt.
Für Anfänger bietet ein Monorepository mehrere wichtige Vorteile:
- Einheitliche Code-Version — alle Abhängigkeiten sind synchronisiert
- Vereinfachtes Refactoring — Änderungen sind sofort in allen Projekten sichtbar
- Standardisierung — einheitliche Einstellungen für Linter, CI/CD und Tests
- Transparenz — leicht verständlich, wie verschiedene Teile des Systems zusammenhängen
In diesem Artikel werden wir untersuchen, wie man ein Monorepository in Go richtig organisiert, angefangen bei der grundlegenden Struktur bis hin zu praktischen Beispielen.
Grundlegende Struktur eines Monorepository in Go
Die richtige Code-Organisation ist die Grundlage eines erfolgreichen Monorepository. Betrachten wir eine typische Struktur:
my-monorepo/├── apps/│ ├── web-api/ # Hauptanwendung│ │ ├── main.go│ │ ├── go.mod│ │ └── internal/│ └── worker/ # Hintergrundverarbeiter│ ├── main.go│ └── go.mod├── libs/│ ├── config/ # Gemeinsame Konfiguration│ │ ├── config.go│ │ └── go.mod│ └── logger/ # Protokollierung│ ├── logger.go│ └── go.mod├── scripts/ # Hilfsskripte├── docs/ # Dokumentation├── go.work # Go workspace└── README.mdWichtige Prinzipien dieser Struktur:
- apps/ — ausführbare Anwendungen (Binärdateien)
- libs/ — wiederverwendbare Bibliotheken
- scripts/ — Automatisierung und Hilfsprogramme
- docs/ — Projektdokumentation
Einrichtung des Go Workspace für ein Monorepository
Seit Go 1.18 gibt es offizielle Unterstützung für Workspaces über die Datei go.work. Dies ermöglicht die gleichzeitige Arbeit mit mehreren Modulen, ohne diese in ein entferntes Repository veröffentlichen zu müssen.
Erstellen wir einen Workspace für unser Monorepository:
// go.workgo 1.22
use ( ./apps/web-api ./apps/worker ./libs/config ./libs/logger)Nach der Erstellung von go.work sind alle Module aus der use-Liste lokal füreinander verfügbar. Dies ist besonders praktisch bei der Entwicklung – Änderungen in einer Bibliothek sind sofort in den Anwendungen sichtbar.
Initialisierung der Module
Für jede Komponente muss ein eigener go.mod erstellt werden. Beispiel für die Konfigurationsbibliothek:
// libs/config/go.modmodule github.com/your-org/libs/config
go 1.22Und für die Anwendung web-api:
// apps/web-api/go.modmodule github.com/your-org/apps/web-api
go 1.22
require github.com/your-org/libs/config v0.0.0
replace github.com/your-org/libs/config => ../../libs/configBeachten Sie das replace – es weist Go an, die lokale Version der Bibliothek zu verwenden, anstatt sie im Internet zu suchen.
Codebeispiel: Bibliothek und Anwendung
Erstellen wir eine einfache Konfigurationsbibliothek und eine Anwendung, die diese verwendet.
Bibliothek libs/config
// libs/config/config.gopackage config
import ( "fmt" "os")
type Config struct { Port string Env string}
func Load() (*Config, error) { port := os.Getenv("PORT") if port == "" { port = "8080" }
env := os.Getenv("ENV") if env == "" { env = "development" }
return &Config{ Port: port, Env: env, }, nil}
func (c *Config) String() string { return fmt.Sprintf("Config{