Einleitung: Was ist ein Monorepository und warum wird es in Java benötigt
In der Java-Entwicklung gibt es zwei Hauptansätze zur Speicherung von Code: Monorepository (Monorepo) und Multirepository (Multirepo). Ein Monorepository ist eine Strategie, bei der alle Codebasen eines Projekts (Bibliotheken, Dienste, Hilfsprogramme, Konfigurationen) in einem einzigen Repository gespeichert werden. Für Java-Entwickler, die mit großen Unternehmenssystemen oder Microservices arbeiten, wird das Monorepository zunehmend zu einer beliebten Lösung.
Die Vorteile eines Monorepositorys in Java liegen auf der Hand:
- Einheitliche Abhängigkeitsversionen: Alle Module verwenden eine Version der Bibliotheken, was Konflikte (classpath hell) ausschließt.
- Vereinfachtes Refactoring: Eine API-Änderung in einem Modul ist sofort in allen abhängigen Modulen sichtbar.
- Durchgängiger Änderungsverlauf: Ein einzelner Commit kann Änderungen in mehreren Modulen enthalten, was das Debugging vereinfacht.
- Standardisierung: Einheitliche Regeln für Codestil, Linting und CI/CD für das gesamte Projekt.
Ein Monorepository erfordert jedoch eine durchdachte Organisation. In diesem Artikel werden wir uns ansehen, wie man ein Java-Projekt in einem Monorepository mit Maven und Gradle richtig strukturiert.
Hauptteil: Organisation eines Monorepositorys in Java
1. Verzeichnisstruktur: Modularer Ansatz
Die Grundlage eines Monorepositorys ist eine modulare Struktur. Jedes Modul stellt eine unabhängige Einheit dar (Bibliothek, Dienst, Hilfsprogramm). Betrachten wir eine typische Struktur für ein Java-Projekt:
my-monorepo/├── build.gradle # Root-Build-Datei (Gradle)├── settings.gradle # Liste der Module├── gradle.properties # Gemeinsame Einstellungen├── gradle/│ └── wrapper/├── common/ # Gemeinsame Bibliotheken│ ├── common-utils/ # Hilfsprogramme (Strings, Datumsangaben)│ └── common-dto/ # DTOs für den Datenaustausch├── services/ # Microservices│ ├── user-service/ # Benutzerservice│ └── order-service/ # Bestellservice├── libs/ # Interne Bibliotheken│ └── logging-lib/ # Logging-Bibliothek├── tests/ # Integrations-/End-to-End-Tests│ └── e2e-tests/└── buildSrc/ # Gemeinsame Plugins und KonfigurationenJedes Modul hat intern eine Standard-Maven/Gradle-Struktur:
user-service/├── build.gradle├── src/│ ├── main/│ │ ├── java/│ │ │ └── com/example/user/│ │ │ ├── controller/│ │ │ ├── service/│ │ │ └── repository/│ │ └── resources/│ │ └── application.yml│ └── test/│ └── java/└── Dockerfile2. Abhängigkeitsverwaltung: Maven BOM und Gradle Version Catalog
Das Hauptproblem eines Monorepositorys ist die Synchronisation der Abhängigkeitsversionen. In Java wird dies auf zwei Arten gelöst.
Variante 1: Maven BOM (Bill of Materials)
Es wird ein Root-POM erstellt, das alle Versionen der Abhängigkeiten deklariert. Untergeordnete Module verwenden dependencyManagement:
<!-- pom.xml (Root) --><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.2.0</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.example</groupId> <artifactId>common-utils</artifactId> <version>1.0.0</version> </dependency> </dependencies></dependencyManagement>Variante 2: Gradle Version Catalog
In der Datei gradle/libs.versi