Modularität ist ein Prinzip, bei dem eine Anwendung aus unabhängigen Modulen zusammengesetzt wird, die jeweils für eine einzelne Funktionalität verantwortlich sind. Laut Android Developers beschleunigt die Aufteilung in Module den Build durch parallele Kompilierung und ermöglicht es Teams, unabhängig an verschiedenen Teilen der Anwendung zu arbeiten. Die modulare Architektur ist zum Standard für große Mobilprojekte mit Dutzenden von Entwicklern geworden.
Wichtige Erkenntnisse
Modularität ist eine Möglichkeit, Code zu organisieren, bei der eine Anwendung aus lose gekoppelten Modulen besteht, die jeweils eine streng definierte Funktionalität über eine öffentliche Schnittstelle bereitstellen. Im Gegensatz zur monolithischen Architektur, bei der sich alle Klassen in einem einzigen Projekt befinden, teilt der modulare Ansatz den Code in physisch unabhängige Baueinheiten auf.
Das Hauptziel der Modularität ist das Komplexitätsmanagement. Ein Entwickler kann sich auf ein Modul konzentrieren, ohne die gesamte Codebasis im Kopf behalten zu müssen. Jedes Modul hat seinen eigenen Verantwortungsbereich und kann unabhängig von den anderen entwickelt, getestet und bereitgestellt werden. Dies ist besonders wertvoll in Projekten mit 10+ Entwicklern, bei denen paralleles Arbeiten an einem Monolithen zu häufigen Merge-Konflikten führt.
Es ist wichtig, Modularität von der Schichtenarchitektur zu unterscheiden. Schichten (Presentation, Domain, Data) teilen Code nach technischen Kriterien, während Module nach funktionalen Kriterien teilen. Ein Modul „Benutzerprofil“ kann eigene Schichten enthalten. In der Praxis werden der modulare Ansatz und die Schichtenarchitektur kombiniert: Jedes Modul hat seine eigene dreischichtige Struktur.
Feature-Module sind die beliebteste Art von Modulen. Jeder Bildschirm oder jede Gruppe verwandter Bildschirme wird in ein eigenes Modul ausgelagert: Onboarding, Profile, Settings, Feed. Ein Feature-Modul enthält alles, was für die Funktion benötigt wird: UI, Geschäftslogik, Datenschicht. Die Modulgrenzen sind geschützt — andere Funktionen können nicht auf seine internen Klassen zugreifen.
Core-Module enthalten die gemeinsame Infrastruktur: Netzwerk, Datenbank, Analyse, Designsystem. Sie hängen nicht von Feature-Modulen ab, aber Feature-Module hängen von ihnen ab. Diese Trennung garantiert, dass eine Änderung des Analyse-SDK die Netzwerkschicht nicht beeinträchtigt und umgekehrt. Core-Module werden ohne Code-Duplizierung zwischen Funktionen wiederverwendet.
Shared-Module enthalten Code, der von mehreren Funktionen verwendet wird: Datenmodelle, Hilfsprogramme, Konstanten, benutzerdefinierte Views. Das Hauptproblem von Shared-Modulen ist das Risiko, zu einer Müllkippe (“Misc-Modul”) zu werden, in der sich mit der Zeit heterogener Code ansammelt. Regel: Ein Shared-Modul muss ein klares Thema haben, zum Beispiel “shared-ui” oder “shared-models”.
Unter Android werden Shared-Module oft in Bibliotheken mit dem Präfix lib ausgelagert: lib-network, lib-database, lib-ui-components. Unter iOS übernehmen interne Swift Packages innerhalb eines Workspace dieselbe Funktion. In der Praxis begrenzen Teams die Anzahl der Shared-Module auf 3–5, um ein übermäßiges Abhängigkeitsnetzwerk zu vermeiden, das den Build erschwert.
Separate Test-Module ermöglichen es, Tests nur für das geänderte Modul auszuführen, ohne die gesamte Testsuite durchlaufen zu lassen. Dies reduziert die CI/CD-Pipeline-Zeit von Stunden auf Minuten. Die Isolierung auf Modulebene gewährleistet SoC auf Bauebene: Ein Netzwerkschicht-Modul kann nicht versehentlich UI-Bibliotheken in seinen Tests importieren.
Jedes Modul muss eine klar definierte öffentliche API haben. Unter Android wird dies durch Zugriffsmodifizierer und api vs implementation in Gradle erreicht. Unter iOS durch public/internal Zugriffsmodifizierer und verwaltete Abhängigkeiten über Package.swift. Die Sichtbarkeit auf das notwendige Minimum zu reduzieren, ist eine Schlüsselpraxis des modularen Designs.
Gradle unterstützt modulare Architektur nativ: Jedes Modul ist eine separate Baueinheit mit eigener build.gradle-Datei. Android-Projekte verwenden eine Kombination aus einem Anwendungsmodul (app) und mehreren Bibliotheksmodulen. Bibliotheksmodule können nicht als Anwendung ausgeführt, aber als AAR in ein Repository veröffentlicht werden.
Eine Schlüsselfunktion von Gradle ist das parallele Bauen unabhängiger Module. Wenn die Module A, B und C nicht voneinander abhängen, kompiliert Gradle sie gleichzeitig unter Nutzung aller CPU-Kerne. In Projekten mit 20+ Modulen reduziert dies einen vollständigen Build von 15 auf 3–5 Minuten. Inkrementelle Builds eines geänderten Moduls dauern Sekunden.
Gradle bietet zwei Arten von Abhängigkeiten zwischen Modulen: api (transitiv) und implementation (nicht transitiv). Der Unterschied ist für die Modularität von entscheidender Bedeutung: implementation verbirgt transitive Abhängigkeiten vor den Konsumenten des Moduls. Wenn das Modul :profile :networking über implementation verwendet, wissen die Konsumenten von :profile nichts von :networking und können nicht darauf zugreifen.
// settings.gradle — Moduldeklaration
include ':app'
include ':feature:profile'
include ':feature:settings'
include ':core:network'
include ':core:database'
// build.gradle feature/profile — Modulabhängigkeiten
dependencies {
implementation project(':core:network')
implementation project(':core:database')
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0'
}
Der Code zeigt die Struktur eines modularen Android-Projekts. Settings.gradle listet alle Module auf, und die build.gradle jedes Feature-Moduls gibt nur die Core-Module an, die es benötigt. Das Build-System löst automatisch transitive Abhängigkeiten auf und baut die Module in der richtigen Reihenfolge.
Swift Package Manager (SPM) ist seit 2019 das Standard-Werkzeug für Modularität in iOS. SPM ermöglicht die Aufteilung einer Anwendung in Swift Packages, die jeweils eine Bibliothek oder eine ausführbare Datei sein können. Ein Package definiert Module (Targets) und ihre Abhängigkeiten über Package.swift. SPM ist in Xcode integriert und erfordert keine zusätzlichen Werkzeuge.
CocoaPods bleibt der wichtigste Abhängigkeitsmanager für Bibliotheken von Drittanbietern. Podfile und Podspec definieren die modulare Struktur, und CocoaPods generiert einen Workspace mit separaten Pod-Projekten. Für die eigene Projektmodularität wählen Teams zunehmend SPM, da es in Xcode integriert ist und keine Installation erfordert.
In der iOS-Modularität spielt die Zugriffskontrolle eine wichtige Rolle: public, package, internal, fileprivate und private. Ein Modul veröffentlicht nur die Typen, die für andere Module zugänglich sein sollen. Interne Implementierungsdetails werden hinter internal- und private-Modifizierern verborgen. Dies verhindert versteckte Abhängigkeiten zwischen Modulen.
// Package.swift — modulare Struktur eines iOS-Projekts
let package = Package(
name: "MyApp",
platforms: [.iOS(SupportedPlatform.iOSVersion.v17)],
products: [
.library(name: "ProfileFeature", targets: ["ProfileFeature"]),
.library(name: "NetworkCore", targets: ["NetworkCore"]),
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.9.0")
],
targets: [
.target(name: "ProfileFeature", dependencies: ["NetworkCore"]),
.target(name: "NetworkCore", dependencies: ["Alamofire"]),
]
)
Package.swift deklariert zwei Bibliotheksprodukte: ProfileFeature und NetworkCore. ProfileFeature hängt von NetworkCore ab, weiß aber nichts von der Existenz von Alamofire — es ist innerhalb von NetworkCore verborgen. Eine solche Isolation ist eine direkte Anwendung von SoC auf Modulebene: Änderungen am HTTP-Client erfordern keine Neukompilierung von ProfileFeature.
Der Hauptvorteil der Modularität ist die Entwicklungsgeschwindigkeit. Teams arbeiten parallel an verschiedenen Modulen ohne Codekonflikte. Die CI/CD-Pipeline baut nur die geänderten Module und führt nur deren Tests aus. Die Feedback-Zeit verkürzt sich und die Release-Häufigkeit steigt. Spotify, Uber und Airbnb veröffentlichten Fallstudien zur Migration auf modulare Architektur mit 2- bis 3-fachen metrischen Verbesserungen.
Der zweite Vorteil ist die Fehlerisolierung. Ein Fehler im Modul Profile wirkt sich nicht auf das Modul Payments aus, wenn keine direkten Abhängigkeiten zwischen ihnen bestehen. Dies ist besonders wichtig in Anwendungen mit Funktionen mit hohem Risiko (Zahlungen, medizinische Daten), bei denen ein Fehler in einem nicht zusammenhängenden Bildschirm die Veröffentlichung kritischer Funktionalität nicht blockieren sollte.
Die Hauptherausforderung ist das Abhängigkeitsmanagement. Bei schlechtem Design entsteht ein Modulgraph, bei dem die Änderung eines Moduls kaskadenartig Dutzende andere neu baut. Die Lösung besteht darin, der Azyklizitätsregel zu folgen: Der Modulabhängigkeitsgraph muss ein gerichteter azyklischer Graph (DAG) sein. Werkzeuge wie Gradle Module Graph Assert helfen, Zyklen zur Build-Zeit zu erkennen.
Die zweite Herausforderung ist die erhöhte anfängliche Einrichtungszeit. Die Erstellung einer modularen Architektur erfordert mehr Zeit in der Projektinitialisierungsphase. Kleine Projekte mit 1–3 Entwicklern können möglicherweise nicht von Modularität profitieren, da sie Zeit mit der Aufrechterhaltung von Modulgrenzen ohne echten Parallelisierungsbedarf verbringen. Die Lösung ist, mit einem Monolithen zu beginnen und Module zu extrahieren, während das Team wächst.
Der Feature-First-Ansatz gruppiert Module nach Funktionalität: Jeder Bildschirm oder jede Bildschirmgruppe wird zu einem separaten Modul. Der Layer-First-Ansatz teilt Code nach technischen Kriterien: separate Module für UI, Geschäftslogik und Daten. In der Praxis entscheiden sich die meisten Teams für Feature-First mit Core-Modulen — dies bietet bessere Isolierung und klare Projektnavigation.
Die Wahl zwischen den Ansätzen hängt von der Teamgröße und der Vorhersagbarkeit der Funktionalität ab. Wenn Sie genau wissen, welche Bildschirme im Projekt enthalten sein werden, ermöglicht Feature-First jedem Entwickler, für sein eigenes Modul verantwortlich zu sein. Wenn sich die Funktionalität häufig ändert und sich zwischen Bildschirmen überschneidet, bietet Layer-First mehr Flexibilität bei der Wiederverwendung von Code zwischen verschiedenen Funktionen.
Häufig gestellte Fragen
Die optimale Anzahl hängt von der Projekt- und Teamgröße ab. Für ein Team von 5 Personen sind 6–10 Module ausreichend. Für 20+ Entwickler sind es 20–40 Module. Regel: Ein Modul sollte klein genug sein, damit ein Entwickler es vollständig verstehen kann, und groß genug, um kein übermäßiges Abhängigkeitsnetzwerk zu schaffen.
Richtige Modularität beschleunigt den Build durch parallele Kompilierung und Caching. Eine übermäßige Anzahl von Modulen mit engen Abhängigkeiten verlangsamt jedoch den Build — Gradle und Xcode verbringen Zeit mit der Auflösung des Graphen. Der Schlüssel zu schnellen Builds ist die Minimierung transitiver Abhängigkeiten und die Einhaltung der Azyklizität.
Ja, aber iterativ. Beginnen Sie mit der Extraktion von Core-Modulen (Netzwerk, Datenbank), dann extrahieren Sie Funktionen eine nach der anderen. Verwenden Sie Feature Flags, um neuen modularen Code parallel zum alten monolithischen Code zu aktivieren. Die vollständige Migration einer großen Anwendung dauert 3 bis 12 Monate.
Module sind Kompilierungseinheiten innerhalb einer einzigen Anwendung. Microservices sind separate Prozesse, die auf verschiedenen Servern laufen. Module teilen Code, Microservices teilen die Laufzeit. In der mobilen Entwicklung wird der Begriff “Microapps” oft als Hybrid verwendet: Feature-Module, die als eigenständige Anwendungen ausgeführt werden können.
Jedes Modul hat seine eigenen Unit-Tests, die unabhängig ausgeführt werden. Integrationstests überprüfen die Interaktion zwischen Modulen. UI-Tests decken Feature-Module mit Mock-Daten ab. Die modulare Architektur vereinfacht das Testen: Das Mocken einer Abhängigkeit eines anderen Moduls ist einfacher als das Mocken eines Teils eines Monolithen.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch