Maven Multi Module
Aus einem unsauberen Monolith-POM wird ein nachvollziehbarer Multi-Module-Build.
Workshop-Slice
Dieses Lab zeigt einen konkreten Enterprise-Fehler mit Vorher/Nachher-Code. Die Beispiele sind bewusst fachlich benannt, damit du Architekturentscheidungen und nicht nur Syntax übst.
Lernziel
Du strukturierst ein Enterprise-Projekt so, dass Domain, Application, Adapter und Architekturtests als getrennte Maven-Module sichtbar werden.
Ausgangslage
Ein einzelnes POM enthält Spring Boot, JPA, Web, Testcontainers, SOAP und OpenShift-Plugins in einem Modul. Dadurch ziehen Domain-Klassen unnötige Framework-Abhängigkeiten und Architekturgrenzen sind nicht sichtbar.
Vorher: problematischer Code
Das Monolith-POM macht alles in einem Modul. Es ist bequem, aber für Enterprise-Lernen und spätere Migration schlecht.
<project>
<artifactId>order-platform</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Analyse: Was ist daran schlecht?
- Domain-Code kann versehentlich Spring/JPA importieren.
- Build-Zeiten und Testarten sind nicht getrennt.
- Adapter-Abhängigkeiten sind nicht erkennbar.
- Spätere Migration zu Quarkus/Jakarta EE wird unnötig schwer.
Der wichtige Punkt ist nicht nur Maven-Technik, sondern Architektur-Sichtbarkeit: Was nicht voneinander abhängen darf, bekommt eigene Module.
Nachher: bessere Lösung
Der Parent steuert Versionen und Module. Domain bleibt frameworkfrei, Adapter hängen bewusst von der Application-Schicht ab.
<project>
<packaging>pom</packaging>
<modules>
<module>shared-kernel</module>
<module>order-domain</module>
<module>order-application</module>
<module>order-adapter-rest</module>
<module>order-adapter-jpa</module>
<module>architecture-tests</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
<project>
<artifactId>order-domain</artifactId>
<dependencies>
<!-- bewusst keine Spring-, JPA- oder Web-Abhängigkeit -->
<dependency>
<groupId>com.acme</groupId>
<artifactId>shared-kernel</artifactId>
</dependency>
</dependencies>
</project>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<scope>test</scope>
</dependency>
Test / Prüfnachweis
Dieser Abschnitt zeigt, wie du die Verbesserung nachweist. Es ist bewusst kein reiner Happy-Path-Test, sondern prüft ein Risiko aus dem Vorher-Teil.
@AnalyzeClasses(packages = "com.acme.order")
class LayerRulesTest {
@ArchTest
static final ArchRule domain_must_not_depend_on_spring =
noClasses().that().resideInAPackage("..domain..")
.should().dependOnClassesThat().resideInAnyPackage("org.springframework..", "jakarta.persistence..");
}
Typische Fehler
- Jedes Modul importiert wieder alle Starter.
- Parent-POM enthält Buildlogik, die nur für ein einzelnes Adaptermodul gilt.
- Tests liegen nur im Webmodul und prüfen Domainregeln indirekt.
Deep-Learning-Bezug
Die Links führen zum ausführlichen Inhalt; die Deep-Learning-Seite bleibt nur die Lernlandkarte und kopiert den Inhalt nicht doppelt.
Prüfcheckliste
- Domain-POM hat keine Spring/JPA/Web-Abhängigkeit.
- Parent-POM enthält dependencyManagement.
- Module bilden Architekturgrenzen ab.
- ArchUnit-Regel verhindert Rückfall in Framework-Kopplung.