Core-Module: Geteilten Code sauber strukturieren
Core-Module bündeln stabilen, wiederverwendbaren Code in einer Mehr-Modul-App. Du lernst, wie du Utilities und Design-Systeme klar abgrenzt.
Sobald eine Android-App über ein einziges Modul hinauswächst und in Feature-Module aufgeteilt wird, entsteht eine zentrale Frage: Wohin kommt der Code, der überall gebraucht wird, aber zu keiner konkreten User Journey gehört? Core-Module sind die Antwort der offiziellen Android-Architektur-Empfehlungen – sie bündeln stabilen, geteilten Code und halten gleichzeitig die Feature-Module fokussiert und schlank.
Was ist das?
Core-Module sind dedizierte Gradle-Module in einer Mehr-Modul-Android-App, die keiner einzigen Feature-Domäne gehören, aber von vielen Feature-Modulen als Abhängigkeit eingebunden werden können. Sie bilden eine eigene Schicht unterhalb der Feature-Module und oberhalb externer Bibliotheken.
Typische Vertreter sind:
- :core:designsystem – Farben, Typografie, Compose-Theme, wiederverwendbare UI-Komponenten
- :core:network – Retrofit- oder Ktor-Setup, Interceptoren, generische Fehlerbehandlung
- :core:data – gemeinsame Datenmodelle, Repository-Schnittstellen, Datenbankmigrationen
- :core:common – zustandslose Extensions, Datums- und Zahlenformatierung, rein logische Hilfsmethoden
Der Begriff „Core” signalisiert Stabilität: Dieser Code ändert sich selten und hat keine Abhängigkeit zu Feature-Modulen. Feature-Module schauen im Abhängigkeitsgraphen nach unten in Richtung Core – nie umgekehrt.
Wie funktioniert es?
In einem Gradle-Multi-Module-Projekt wird ein Core-Modul als com.android.library-Plugin angelegt, genau wie ein Feature-Modul. Feature-Module deklarieren Core-Module als implementation-Abhängigkeit in ihrer build.gradle.kts. Gradle sorgt dafür, dass nur die öffentliche API des Core-Moduls sichtbar ist – interne Implementierungsdetails bleiben verborgen.
Das Kernprinzip ist der unidirektionale Abhängigkeitsgraph: Feature-Module können Core-Module einbinden, aber Core-Module dürfen niemals Feature-Module importieren. Diese klare Einbahnstraße verhindert zyklische Abhängigkeiten, die Gradle-Builds blockieren und Testbarkeit zerstören. Du erkennst eine Verletzung dieses Prinzips sofort, wenn Gradle beim Sync eine „Circular dependency”-Warnung wirft.
Zur Laufzeit passiert nichts Besonderes – die kompilierten Klassen landen wie jeder andere Library-Code im APK oder AAB. Der Mehrwert liegt vollständig in der Build- und Entwicklungszeit: kürzere inkrementelle Kompilierung, weil sich stabile Core-Module selten ändern und im Build-Cache verbleiben, klare Eigentümerschaft für Teams, und die Möglichkeit, Core-Logik vollständig isoliert mit Unit-Tests abzusichern.
In der Praxis
Beispiel: Design-System-Modul aufsetzen
// :core:designsystem – Color.kt
package de.androidcoden.core.designsystem.theme
import androidx.compose.ui.graphics.Color
val AndroidGreen = Color(0xFF3DDC84)
val OnBackground = Color(0xFF1C1B1F)
val SurfaceLight = Color(0xFFFAFAFA)
// :core:designsystem – AppTheme.kt
@Composable
fun AppTheme(content: @Composable () -> Unit) {
MaterialTheme(
colorScheme = lightColorScheme(
primary = AndroidGreen,
onBackground = OnBackground,
surface = SurfaceLight
),
content = content
)
}
Ein Feature-Modul bindet das Design-System dann ohne weiteren Aufwand ein:
// :feature:home/build.gradle.kts
dependencies {
implementation(projects.core.designsystem)
implementation(projects.core.common)
}
Stolperfalle: Der Core-Dump
Die größte Gefahr bei Core-Modulen ist, dass sie zu einem Sammelbecken werden. Wenn jedes neue Utility reflexartig in :core:common landet, wächst das Modul unkontrolliert – die Kompilierungszeit steigt, der Abhängigkeitsbaum wird undurchsichtig, und Code-Ownership verschwindet.
Faustregel: Ein Stück Code gehört in ein Core-Modul, wenn mindestens zwei unabhängige Feature-Module ihn tatsächlich benötigen – nicht, weil er irgendwann vielleicht nützlich sein könnte. Alles andere bleibt im Feature-Modul, bis die zweite Verwendung real auftaucht.
Ebenso gefährlich ist ein einzelnes monolithisches :core-Modul, das alle geteilten Belange vereint. Teile stattdessen nach Verantwortlichkeit auf: :core:ui, :core:network, :core:domain. So bleibt jedes Modul klein genug, um schnell zu kompilieren, und verständlich genug, um neuen Teammitgliedern sofort zu erklären, was darin zu suchen ist.
Fazit
Core-Module sind das Rückgrat jeder gut strukturierten Mehr-Modul-Android-App. Sie machen geteilten Code explizit, testbar und auffindbar – aber nur, wenn du aktiv gegen den natürlichen Hang zur Akkumulation vorgehst. Überprüfe deine Core-Module regelmäßig: Gibt es Klassen, die ausschließlich von einem einzigen Feature-Modul genutzt werden? Dann gehören sie dorthin. Führe ./gradlew :core:common:test aus, um sicherzustellen, dass deine Utilities isoliert testbar bleiben, und behandle Änderungen an Core-Modulen im Code-Review mit besonderer Sorgfalt – jede Modifikation dort wirkt sich potenziell auf die gesamte App aus.