Gradle folgt dem Prinzip “Convention over Configuration”, das ursprünglich durch Ruby on Rails popularisiert wurde. Dieses Konzept bedeutet, dass Gradle sinnvolle Standardwerte für alle Aspekte eines Builds vorgibt, diese aber bei Bedarf überschrieben werden können. Die Konventionen reduzieren die notwendige Konfiguration erheblich und sorgen für einheitliche Projektstrukturen über verschiedene Teams und Projekte hinweg.
Die Konventionen werden durch Plugins eingeführt. Das Java-Plugin
beispielsweise definiert, dass Java-Quellcode unter
src/main/java liegt, kompilierte Klassen nach
build/classes/java/main geschrieben werden und das finale
JAR-Archiv als build/libs/projektname-version.jar erstellt
wird. Diese Konventionen basieren auf jahrelanger Erfahrung der
Java-Community und haben sich als Best Practices etabliert.
| Aspekt | Standard-Konvention | Überschreibbar via |
|---|---|---|
| Source Code | src/main/java |
sourceSets.main.java.srcDirs |
| Test Code | src/test/java |
sourceSets.test.java.srcDirs |
| Ressourcen | src/main/resources |
sourceSets.main.resources.srcDirs |
| Test-Ressourcen | src/test/resources |
sourceSets.test.resources.srcDirs |
| Kompilierte Klassen | build/classes/java/main |
sourceSets.main.output.classesDirs |
| Test-Klassen | build/classes/java/test |
sourceSets.test.output.classesDirs |
| JAR-Datei | build/libs/${project.name}-${version}.jar |
jar.archiveFileName |
| Test-Reports | build/reports/tests |
test.reports.html.outputLocation |
| JavaDoc | build/docs/javadoc |
javadoc.destinationDir |
Der Vorteil dieses Ansatzes zeigt sich besonders beim Onboarding neuer Teammitglieder. Ein Entwickler, der Gradle kennt, findet sich sofort in jedem Gradle-Projekt zurecht, das den Konventionen folgt. Die kognitive Last reduziert sich, da nicht für jedes Projekt neue Strukturen erlernt werden müssen. Gleichzeitig bleibt die Flexibilität erhalten, spezielle Anforderungen durch Konfiguration zu erfüllen.
Source Sets sind ein zentrales Konzept in Gradle zur Organisation von
Quellcode und Ressourcen. Ein Source Set definiert eine logische
Gruppierung von Sourcecode-Verzeichnissen, Ressourcen-Verzeichnissen und
spezifischen Classpaths. Das Java-Plugin konfiguriert standardmäßig zwei
Source Sets: main für Produktivcode und test
für Testcode.
Jedes Source Set verfügt über separate Compile- und
Runtime-Classpaths. Der main Source Set nutzt die
implementation und compileOnly Dependencies,
während der test Source Set zusätzlich auf die kompilierten
Klassen und Ressourcen von main zugreift. Diese Trennung
verhindert, dass Testabhängigkeiten wie JUnit in Produktivcode verwendet
werden können.
Zusätzliche Source Sets werden für spezielle Anforderungen definiert.
Ein typisches Beispiel ist ein integrationTest Source Set
für Integrationstests:
sourceSets {
create("integrationTest") {
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
java.srcDir("src/integrationTest/java")
resources.srcDir("src/integrationTest/resources")
}
}
configurations["integrationTestImplementation"].extendsFrom(configurations.implementation.get())
configurations["integrationTestRuntimeOnly"].extendsFrom(configurations.runtimeOnly.get())Diese Konfiguration erstellt eine separate Testumgebung mit eigenen Abhängigkeiten und Ressourcen, die unabhängig von Unit-Tests ausgeführt werden kann.
| Source Set Name | Verwendungszweck | Typische Dependencies | Ausführung |
|---|---|---|---|
integrationTest |
Integrationstests mit externen Systemen | TestContainers, REST-assured, WireMock | Nach Unit-Tests |
functionalTest |
End-to-End Tests der Anwendung | Selenium, Cucumber, Appium | Nach Integration Tests |
performanceTest |
Last- und Performance-Tests | JMeter, Gatling, Apache Bench | Manuell/CI-Pipeline |
contractTest |
API Contract Tests | Pact, Spring Cloud Contract | Parallel zu Unit-Tests |
smokeTest |
Minimale Validierung nach Deployment | REST-assured, Minimal-Set | Nach Deployment |
| Projekttyp | Hauptplugin | Charakteristische Verzeichnisse | Besonderheit |
|---|---|---|---|
| Java-Bibliothek | java-library |
src/main/java, src/test/java |
Standard-Struktur |
| Java-Anwendung | application |
Standard + Main-Class | Ausführbare JAR |
| Web-Anwendung | war |
src/main/webapp |
WAR-Deployment |
| Spring Boot | spring-boot |
src/main/resources/application.yml |
Embedded Server |
| Kotlin | kotlin |
src/main/kotlin |
Parallel zu Java |
| Android | com.android.application |
src/main/res, AndroidManifest.xml |
Varianten-Struktur |
Die Projektstruktur variiert je nach verwendeten Plugins und
Projekttyp. Ein Standard-Java-Bibliotheksprojekt folgt der bereits
beschriebenen Struktur mit src/main und
src/test. Webanwendungen, die das War-Plugin verwenden,
erweitern diese Struktur um src/main/webapp für statische
Web-Ressourcen wie HTML, CSS und JavaScript-Dateien.
Kotlin-Projekte nutzen parallele Verzeichnisse zu Java. Der
Kotlin-Code liegt in src/main/kotlin und
src/test/kotlin, kann aber problemlos mit Java-Code in den
entsprechenden Java-Verzeichnissen koexistieren. Diese Struktur
ermöglicht eine graduelle Migration von Java zu Kotlin oder die
Verwendung beider Sprachen im selben Projekt.
Android-Projekte weichen stärker von der Standard-Struktur ab. Sie
verwenden src/main/java für Code, aber auch
src/main/res für Android-Ressourcen und
src/main/AndroidManifest.xml für die Manifest-Datei.
Build-Varianten wie debug und release erhalten
eigene Verzeichnisse unter src/debug und
src/release. Diese Struktur reflektiert die spezifischen
Anforderungen der Android-Plattform.
Spring Boot Projekte folgen der Standard-Java-Struktur, ergänzen
diese aber häufig um
src/main/resources/application.properties oder
application.yml für die Konfiguration. Die
static und templates Verzeichnisse unter
src/main/resources enthalten statische Ressourcen und
View-Templates für Webanwendungen.
Das build Verzeichnis ist der zentrale Ort für alle
generierten Dateien. Gradle strukturiert dieses Verzeichnis
systematisch, um verschiedene Arten von Output zu organisieren. Unter
build/classes liegen die kompilierten Klassen, getrennt
nach Sprache und Source Set. Das Verzeichnis
build/generated enthält generierten Sourcecode,
beispielsweise von Annotation Processors oder Code-Generatoren.
| Verzeichnis | Inhalt | Beispiele |
|---|---|---|
build/classes |
Kompilierte Klassen | java/main/, java/test/,
kotlin/main/ |
build/resources |
Verarbeitete Ressourcen | main/, test/ |
build/generated |
Generierter Code | sources/annotationProcessor/ |
build/libs |
Finale Artefakte | projekt-1.0.jar,
projekt-1.0-sources.jar |
build/reports |
Test- und Analyse-Reports | tests/test/index.html, jacoco/ |
build/docs |
Dokumentation | javadoc/, kdoc/ |
build/tmp |
Temporäre Dateien | compileJava/, jar/,
expandedArchives/ |
build/distributions |
Distributions-Archive | projekt-1.0.zip, projekt-1.0.tar |
Die Struktur build/reports sammelt verschiedene Berichte
wie Test-Reports, Coverage-Reports oder Dependency-Reports.
HTML-Test-Reports finden sich unter
build/reports/tests/test/index.html und bieten eine
detaillierte Übersicht über Testergebnisse. Das Verzeichnis
build/libs enthält die finalen Artefakte wie JAR- oder
WAR-Dateien.
Gradle nutzt build/tmp für temporäre Dateien während der
Task-Ausführung. Jeder Task kann hier eigene temporäre Verzeichnisse
anlegen. Das Verzeichnis build/resources spiegelt die
verarbeiteten Ressourcen wider, nachdem Processing-Tasks wie das
Ersetzen von Tokens ausgeführt wurden.
Große Projekte profitieren von einer Aufteilung in mehrere Module.
Ein typisches Multi-Modul-Projekt strukturiert sich hierarchisch mit
einem Root-Projekt und mehreren Subprojekten. Die
settings.gradle.kts im Root-Verzeichnis definiert die
Projektstruktur:
rootProject.name = "enterprise-application"
include("core", "api", "web", "data-access")Jedes Modul erhält ein eigenes Verzeichnis mit eigener
build.gradle.kts Datei. Die Verzeichnisstruktur spiegelt
die logische Architektur der Anwendung wider. Das core
Modul enthält Domänenlogik, api definiert Schnittstellen,
data-access kapselt Datenbankzugriffe und web
implementiert die Präsentationsschicht.
Module können voneinander abhängen. Das web Modul
deklariert eine Abhängigkeit zum core Modul mit
implementation(project(":core")). Gradle stellt sicher,
dass Module in der richtigen Reihenfolge gebaut werden und Änderungen in
einem Modul automatisch abhängige Module triggern.
Gemeinsame Konfiguration wird im Root-Projekt zentralisiert. Die
Root-build.gradle.kts kann Konfiguration auf alle oder
spezifische Subprojekte anwenden:
subprojects {
apply(plugin = "java")
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
repositories {
mavenCentral()
}
}Gradle-Projekte folgen etablierten Namenskonventionen, die Konsistenz
und Wartbarkeit fördern. Projektnamen verwenden Kleinbuchstaben mit
Bindestrichen als Trennzeichen, beispielsweise
customer-service oder payment-gateway. Diese
Namen erscheinen in generierten Artefakten und sollten daher
aussagekräftig und eindeutig sein.
| Element | Konvention | Beispiele richtig | Beispiele falsch |
|---|---|---|---|
| Projektnamen | kebab-case | customer-service, payment-api |
CustomerService, customer_service |
| Task-Namen | camelCase | generateDocs, deployToProduction |
generate-docs, deploy_to_production |
| Properties | camelCase | maxHeapSize, buildNumber |
max_heap_size, MaxHeapSize |
| Konstanten | UPPER_SNAKE_CASE | DEFAULT_TIMEOUT, MAX_CONNECTIONS |
defaultTimeout, max-connections |
| Verzeichnisse | lowercase | src, test, docs |
SRC, Test, DOCS |
| Konfigurationsdateien | kebab-case mit Suffix | application-dev.yml,
gradle-wrapper.properties |
applicationDev.yml,
gradle_wrapper.properties |
| Source Sets | camelCase | integrationTest, functionalTest |
integration-test, integration_test |
| Configurations | camelCase | compileOnly, testImplementation |
compile-only, test_implementation |
Task-Namen folgen der camelCase-Konvention und beschreiben die
ausgeführte Aktion. Verben wie compile, test,
generate oder deploy kennzeichnen den
Task-Zweck. Zusammengesetzte Task-Namen wie compileTestJava
oder generateApiDocumentation verdeutlichen Kontext und
Aktion.
Properties und Variablen in Build-Skripten nutzen ebenfalls
camelCase. Konstanten werden in UPPER_SNAKE_CASE geschrieben.
Extension-Properties für Projekt-Metadaten folgen der Punkt-Notation,
etwa project.ext.apiVersion oder
project.ext.dockerRegistry.
Verzeichnisnamen bleiben konsistent mit den Plugin-Konventionen.
Eigene Verzeichnisse folgen dem gleichen Schema: Kleinbuchstaben, keine
Sonderzeichen, aussagekräftige Namen. Ein Verzeichnis für
Datenbankmigrationen heißt db-migrations, nicht
DB_Migrations oder
database_migration_scripts.
Die Trennung von Konfiguration und Konvention zeigt sich auch in der
Benennung von Konfigurationsdateien. Umgebungsspezifische Properties
nutzen das Schema application-{umgebung}.properties, also
application-dev.properties oder
application-prod.properties. Diese Konvention ermöglicht
automatisches Laden basierend auf aktiven Profilen.