Android Modularisierung 2026: Multi-Modul-Architektur und Interview-Fragen
Best Practices für Android Modularisierung mit Convention Plugins, Gradle Version Catalogs und Feature-Modulen. Inklusive Interview-Fragen für Senior-Entwickler.

Android Modularisierung verwandelt monolithische Codebasen in wartbare, skalierbare Systeme, in denen Teams parallel arbeiten können, ohne sich gegenseitig in die Quere zu kommen. Eine gut modularisierte Android-App baut schneller, ermöglicht isolierte Tests und ermöglicht neuen Entwicklern einen Einstieg in Tagen statt Wochen.
Modularisierung teilt eine Android-App in unabhängige Gradle-Module mit klaren Grenzen auf. Lose Kopplung zwischen Modulen und hohe Kohäsion innerhalb jedes Moduls sind die Leitprinzipien. Googles Now in Android Referenz-App demonstriert dieses Muster mit über 40 Modulen.
Warum Multi-Modul-Architektur für Android-Apps wichtig ist
Eine Android-App mit einem einzigen Modul rekompiliert alles, wenn sich eine Zeile ändert. Eine Änderung an der Netzwerkschicht löst die Rekompilierung der UI aus. Zwei Entwickler, die verschiedene Features bearbeiten, erzeugen Merge-Konflikte in gemeinsam genutzten Dateien. Build-Zeiten dehnen sich auf über 4 Minuten bei inkrementellen Builds aus.
Multi-Modul-Architektur adressiert diese Problemstellen direkt:
- Schnellere Builds: Gradle rekompiliert nur betroffene Module. Eine Änderung in
:feature:checkoutlässt:feature:profileunberührt. - Parallele Entwicklung: Teams besitzen separate Module mit definierten APIs. Merge-Konflikte reduzieren sich erheblich.
- Isolierte Tests: Unit-Tests laufen gegen ein einzelnes Modul, ohne die gesamte App zu laden.
- Code-Wiederverwendung: Ein
:core:uiDesign-System-Modul wird zu einer Bibliothek, die in mehreren Apps verwendbar ist.
Der offizielle Android Modularisierungs-Guide empfiehlt, Module nach Features oder Schichten zu strukturieren, die sich gemeinsam ändern, entsprechend dem Single Responsibility Principle.
Modultypen und ihre Verantwortlichkeiten
Das Now in Android Repository etabliert eine bewährte Modul-Taxonomie, die sich auf Enterprise-Apps skalieren lässt.
include(":app")
// Core-Module - gemeinsam genutzte Utilities
include(":core:common")
include(":core:data")
include(":core:database")
include(":core:datastore")
include(":core:designsystem")
include(":core:domain")
include(":core:model")
include(":core:network")
include(":core:ui")
// Feature-Module - bildschirmspezifische Logik
include(":feature:home")
include(":feature:search")
include(":feature:bookmarks")
include(":feature:settings")App-Modul: Enthält MainActivity, Navigation-Setup und App-Level-Scaffolding. Hängt von allen Feature-Modulen und erforderlichen Core-Modulen ab.
Core-Module: Bieten gemeinsam genutzte Funktionalität über Features hinweg. :core:network behandelt API-Aufrufe. :core:database verwaltet Room-Entities. :core:designsystem definiert die visuellen Komponenten der App.
Feature-Module: Kapseln einen einzelnen Bildschirm oder Benutzerfluss. Jedes Feature-Modul hängt nur von den Core-Modulen ab, die es benötigt, niemals von anderen Feature-Modulen.
Diese Struktur erzwingt unidirektionale Abhängigkeiten: Features hängen von Core ab, und App hängt von Features ab.
Gradle Version Catalogs für Dependency Management
Die Verwaltung von Abhängigkeiten über 25+ Module ohne Versionskonflikte erfordert eine zentralisierte Konfiguration. Gradle Version Catalogs lösen dies mit einer einzigen libs.versions.toml-Datei.
# gradle/libs.versions.toml
[versions]
agp = "8.7.0"
kotlin = "2.1.0"
compose-bom = "2026.09.00"
ksp = "2.1.0-1.0.29"
hilt = "2.54"
room = "2.7.0"
retrofit = "2.11.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.15.0" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
[bundles]
compose = ["androidx-compose-ui", "androidx-compose-material3"]
room = ["room-runtime", "room-ktx"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }Module referenzieren dann Abhängigkeiten mit typsicheren Accessors:
dependencies {
implementation(libs.bundles.compose)
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
}Gradle generiert diese Accessors während der Synchronisation und ermöglicht IDE-Autovervollständigung und Compile-Zeit-Validierung.
Bereit für deine Android-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Convention Plugins: DRY Build-Konfiguration
Ein Projekt mit 25 Modulen ohne Convention Plugins wiederholt dieselben compileSdk, minSdk, composeOptions und Plugin-Anwendungen in jeder build.gradle.kts. Verschiedene Module können versehentlich unterschiedliche Konfigurationen verwenden.
Convention Plugins zentralisieren diese Logik in einem build-logic Included Build.
import com.android.build.api.dsl.LibraryExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.library")
pluginManager.apply("org.jetbrains.kotlin.android")
extensions.configure<LibraryExtension> {
compileSdk = 35
defaultConfig {
minSdk = 26
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
}
}
}Registrierung des Plugins in build-logic/convention/build.gradle.kts:
plugins {
`kotlin-dsl`
}
dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
}
gradlePlugin {
plugins {
register("androidLibrary") {
id = "myapp.android.library"
implementationClass = "AndroidLibraryConventionPlugin"
}
register("androidFeature") {
id = "myapp.android.feature"
implementationClass = "AndroidFeatureConventionPlugin"
}
register("androidHilt") {
id = "myapp.android.hilt"
implementationClass = "AndroidHiltConventionPlugin"
}
}
}Feature-Module erfordern jetzt minimale Konfiguration:
plugins {
alias(libs.plugins.myapp.android.feature)
alias(libs.plugins.myapp.android.hilt)
}
dependencies {
implementation(projects.core.data)
implementation(projects.core.designsystem)
}Googles Now in Android build-logic verwendet genau dieses Muster.
Modul-Kommunikationsmuster
Feature-Module dürfen nicht direkt voneinander abhängen. Diese Einschränkung erhält die parallele Kompilierung und verhindert zirkuläre Abhängigkeiten. Drei Muster handhaben die Feature-übergreifende Kommunikation.
Navigation über gemeinsame Routes
object Routes {
const val HOME = "home"
const val PRODUCT_DETAIL = "product/{productId}"
const val CHECKOUT = "checkout"
fun productDetail(productId: String) = "product/$productId"
}Feature-Module importieren Routes von :core:navigation, ohne zu wissen, welches Modul welches Ziel implementiert.
Gemeinsame Domain-Modelle
data class Product(
val id: String,
val name: String,
val price: BigDecimal,
val imageUrl: String
)Sowohl :feature:catalog als auch :feature:checkout hängen von :core:model für die Product-Klasse ab.
Event Bus oder Shared ViewModel
Für Laufzeit-Kommunikation handhabt ein gemeinsamer Event Bus in :core:common oder ein Scoped ViewModel Feature-übergreifende Events:
object CartEventBus {
private val _events = MutableSharedFlow<CartEvent>()
val events: SharedFlow<CartEvent> = _events.asSharedFlow()
suspend fun emit(event: CartEvent) {
_events.emit(event)
}
}
sealed interface CartEvent {
data class ItemAdded(val productId: String) : CartEvent
data class ItemRemoved(val productId: String) : CartEvent
}Interview-Fragen zur Android Modularisierung
Modularisierung erscheint häufig in Senior Android-Interviews. Interviewer bewerten das Verständnis von Build-Systemen, Architekturentscheidungen und praktischen Trade-offs.
Bei der Diskussion von Modularisierung in Interviews sollten konkrete Metriken referenziert werden: Build-Zeit-Verbesserungen, Teamgeschwindigkeits-Gewinne oder spezifische Modulanzahlen aus Projekten. Abstrakte Antworten über "bessere Trennung der Zuständigkeiten" sind weniger überzeugend als "Build-Zeiten sanken von 4 Minuten auf 45 Sekunden nach der Modularisierung."
F: Was ist der Unterschied zwischen api- und implementation-Abhängigkeiten in Gradle?
implementation-Abhängigkeiten sind modulintern. Konsumenten des Moduls können nicht darauf zugreifen. api-Abhängigkeiten werden transitiv exponiert: Wenn Modul A api(libs.retrofit) verwendet, kann Modul B, das von A abhängt, Retrofit-Klassen direkt verwenden.
Faustregel: Standardmäßig implementation verwenden. Nur api verwenden, wenn die Abhängigkeit Teil der öffentlichen API des Moduls ist. Übermäßige Verwendung von api bricht die Kapselung und verlangsamt Builds.
F: Wie wird die Navigation zwischen Feature-Modulen gehandhabt, die nicht voneinander abhängen?
Ein :core:navigation-Modul erstellen, das Route-Definitionen und Navigations-Interfaces enthält. Feature-Module hängen von diesem gemeinsamen Modul ab. Das App-Modul verbindet Ziele mit Implementierungen. Dies folgt dem Dependency Inversion Principle: Features hängen von Abstraktionen ab, nicht von konkreten Implementierungen.
F: Was sind die Trade-offs bei zu vielen Modulen?
Jedes Modul fügt Gradle-Konfigurations-Overhead hinzu: Build-Dateien, Ressourcenverarbeitung, Manifest-Merging. Initiale Sync-Zeiten steigen. Der Wendepunkt variiert je nach Projekt, aber 50+ Module sind bei großen Apps üblich. Die Vorteile (parallele Builds, isolierte Tests, klare Ownership) überwiegen typischerweise den Overhead für Teams von 5+ Entwicklern.
F: Wie unterscheiden sich Convention Plugins von buildSrc?
buildSrc-Änderungen lösen einen vollständigen Projekt-Rebuild aus. Convention Plugins in einem Included Build rekompilieren nur, wenn sich der Plugin-Code ändert, nicht wenn sich konsumierende Module ändern. Dies macht Convention Plugins schneller für iterative Entwicklung. Zusätzlich können Convention Plugins als separates Artefakt zur Wiederverwendung über Repositories hinweg veröffentlicht werden.
F: Wie wird ein Feature-Modul isoliert getestet?
Feature-Module hängen über Interfaces von Core-Modulen ab. Fake-Implementierungen in Test-Fixtures erstellen:
class FakeProductRepository : ProductRepository {
private val products = mutableListOf<Product>()
override suspend fun getProducts(): List<Product> = products
fun addProduct(product: Product) {
products.add(product)
}
}Feature-Modul-Tests injizieren Fakes über Hilts Testing-APIs oder manuelle Constructor-Injection.
Für weitere Übungsfragen zur Android-Architektur siehe die MVVM-Architektur Interview-Fragen und das Dependency Injection Modul.
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Kernpunkte für Multi-Modul Android-Projekte
- Module nach Feature (
:feature:home,:feature:checkout) und Schicht (:core:data,:core:ui) strukturieren. Features hängen von Core ab, niemals voneinander. - Gradle Version Catalogs (
libs.versions.toml) zur Zentralisierung von Abhängigkeitsversionen verwenden. Bundles gruppieren verwandte Abhängigkeiten für sauberere Build-Dateien. - Convention Plugins in
build-logicimplementieren, um duplizierte Konfiguration über Module hinweg zu eliminieren. Eine Plugin-Änderung aktualisiert alle konsumierenden Module. - Unidirektionale Abhängigkeiten erzwingen: App hängt von Features ab, Features hängen von Core ab. Tools wie Dependency Guard können die Durchsetzung automatisieren.
- Build-Performance vor und nach der Modularisierung messen. Inkrementelle Build-Zeiten tracken, nicht nur Clean-Builds, um Verbesserungen zu validieren.
- Modulgrenzen stabil halten. Häufige Umstrukturierung negiert die Vorteile der Modularisierung.
Findest du den Bug in Android?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 17. September 2026
Teilen
Verwandte Artikel

Android CameraX 2026: Foto- und Videoaufnahme mit Interview-Fragen
CameraX Tutorial für Android-Entwickler: Lifecycle-Management, Foto- und Videoaufnahme mit Jetpack Compose und Interview-Fragen.

Jetpack Navigation Compose 2026: Typsichere Navigation und Interviewfragen
Ein umfassender Leitfaden zur typsicheren Navigation mit Jetpack Compose. Mit praktischen Codebeispielen, Best Practices und häufigen Interviewfragen für Android-Entwickler.

Android WorkManager 2026: Hintergrundaufgaben, Constraints und Interview-Fragen
Umfassende Anleitung zu Android WorkManager 2.11/2.12 mit Kotlin-Codebeispielen, Constraints-Konfiguration, Verkettung von Arbeitsaufträgen und häufigen Fragen aus technischen Vorstellungsgesprächen.