Monorepository mit Rust: Code-Organisation in der Praxis
Ein Monorepository (Monorepo) ist ein Ansatz zur Code-Verwaltung, bei dem mehrere Projekte oder Komponenten in einem einzigen Repository gespeichert werden. Im Rust-Ökosystem ist diese Praxis dank der integrierten Unterstützung von Cargo Workspace besonders beliebt. In diesem Artikel werden wir uns ansehen, wie man ein Monorepository mit Rust in der Praxis organisiert: von der Erstellung der Struktur bis zur Verwaltung von Abhängigkeiten und Tests. Sie erfahren, wie Sie typische Fehler vermeiden und Ihre Codebasis skalierbar machen.
Was ist ein Cargo Workspace und warum wird er benötigt
Cargo Workspace ist ein Mechanismus, der es ermöglicht, mehrere Crates (Bibliotheken oder ausführbare Dateien) innerhalb eines einzigen Projekts zu verwalten. Anstatt separate Repositories für jede Komponente zu erstellen, fassen Sie sie in einer einheitlichen Struktur zusammen. Dies bietet mehrere Vorteile:
- Einheitliches Build-System — ein einziger Befehl
cargo builderstellt alle Crates. - Gemeinsame Abhängigkeiten — doppelte Abhängigkeiten werden nur einmal aufgelöst, was die Größe der endgültigen Binärdatei reduziert.
- Vereinfachtes Testen — Tests für alle Crates können mit einem einzigen Befehl ausgeführt werden.
- Versionskonsistenz — alle Komponenten verwenden dieselben Versionen von Bibliotheken.
Ein Monorepository ist besonders nützlich für Microservices, allgemeine Bibliotheken oder Projekte mit einer klaren Schichtentrennung (z. B. Kern, API, CLI).
Erstellung der Monorepository-Struktur
Beginnen wir mit einem praktischen Beispiel. Angenommen, wir entwickeln ein Aufgabenverwaltungssystem. Wir werden drei Crates haben:
core— eine Bibliothek mit der Geschäftslogik.cli— eine ausführbare Datei für die Kommandozeile.server— ein Webserver (ausführbare Datei).
Zuerst erstellen wir das Stammverzeichnis und die Datei Cargo.toml für den Workspace:
mkdir task_managercd task_manager# Erstellen der Stamm-Cargo.tomltouch Cargo.tomlIn der Stamm-Cargo.toml geben wir den Workspace an:
[workspace]members = [ "core", "cli", "server",]Jetzt erstellen wir jeden Crate mit cargo new:
cargo new core --libcargo new clicargo new serverDanach sieht die Struktur wie folgt aus:
task_manager/├── Cargo.toml # Stamm-Workspace├── core/│ ├── Cargo.toml│ └── src/│ └── lib.rs├── cli/│ ├── Cargo.toml│ └── src/│ └── main.rs└── server/ ├── Cargo.toml └── src/ └── main.rsBeachten Sie: Die Stamm-Cargo.toml enthält keinen Abschnitt [package], sondern nur [workspace]. Dies ist für die korrekte Funktion wichtig.
Verwaltung von Abhängigkeiten zwischen Crates
Eine der Hauptaufgaben in einem Monorepository ist die korrekte Konfiguration von Abhängigkeiten zwischen Crates. In unserem Beispiel hängen cli und server von core ab. Wir geben dies in ihren Cargo.toml-Dateien an:
Für cli/Cargo.toml:
[package]name = "cli"version = "0.1.0"edition = "2021"
[dependencies]core = { path = "../core" }clap = { version = "4.0", features = ["derive"] } # Externe Abhängigkeit für CLIFür server/Cargo.toml:
[package]name = "server"version = "0.1.0"edition = "2021"
[dependencies]core = { path = "../core" }actix-web = "4" # Externe Abhängigkeit für den WebserverIn der Stamm-Cargo.toml können auch gemeinsame Abhängigkeiten angegeben werden, falls sie von allen Crates benötigt werden, aber normalerweise erfolgt dies individuell. Der Workspace ermöglicht jedoch die Auflösung von Versionskonflikten: Wenn zwei Crates dieselbe Bibliothek verwenden, zieht Cargo automatisch eine Version