Repository Pattern — ein Muster, das eine Abstraktionsschicht zwischen der Geschäftslogik und den Datenquellen hinzufügt. Anstatt direkt API, Datenbank oder Cache aufzurufen, bietet das Repository eine einheitliche Schnittstelle zum Abrufen und Speichern von Daten. Dies vereinfacht Tests und das Wechseln zwischen Quellen. Lesen Sie mehr in der Android Data Layer-Dokumentation.
Wichtige Punkte
Repository Pattern ist ein strukturelles Muster, das die Geschäftslogik vom direkten Zugriff auf Datenquellen isoliert. Anstatt dass eine Activity, UIViewController oder ViewModel direkt Retrofit, URLSession, Room oder CoreData aufruft, kommunizieren sie mit dem Repository. Das Repository entscheidet, woher die Daten kommen — aus dem Netzwerk, der Datenbank oder dem Cache — und gibt das Ergebnis in einem einheitlichen Format zurück. Dies implementiert das Prinzip der einzigen Verantwortung — die UI weiß nicht, wie oder woher die Daten stammen.
Repository-Komponenten umfassen ein Interface (Protokoll), eine Implementierung und eine oder mehrere DataSources. Eine DataSource ist eine Klasse, die mit einer einzigen Quelle arbeitet: RemoteDataSource ruft die API über einen HTTP-Client auf, LocalDataSource liest und schreibt in die Datenbank. Das Repository erhält die DataSources über den Konstruktor (Dependency Injection) und entscheidet, welche Quelle verwendet wird. Bei der Anforderung einer Benutzerliste prüft das Repository beispielsweise zuerst den Cache, dann die Datenbank, dann das Netzwerk.
Vorteile des Repository Pattern: Isolation von Datenquellenänderungen (API-Änderungen, DB-Migrationen) wirkt sich nicht auf die UI-Schicht aus; Unit-Testing durch Ersetzen von Repository oder DataSource; Caching ist für die UI transparent; Wechsel zwischen Online- und Offline-Modi ohne Änderung der Bildschirmlogik. Die Android-Community empfiehlt Repository als obligatorische Schicht in Clean Architecture.
iOS-Implementierung des Repository basiert auf Swift-Protokollen. Das Repository-Protokoll deklariert Methoden zum Abrufen und Speichern von Daten. Die tatsächliche Implementierung wird über den Initialisierer injiziert — dies ermöglicht das Ersetzen der Implementierung in Tests und SwiftUI-Vorschauen. DataSources werden ebenfalls als Protokolle deklariert: Protocol RemoteDataSource, Protocol LocalDataSource. ViewModel oder Interactor kennen keine spezifische Implementierung — nur das Repository-Protokoll.
protocol UserRepository {
func getUsers() async throws -> [User]
}
protocol UserRemoteDataSource {
func fetchUsers() async throws -> [User]
}
protocol UserLocalDataSource {
func getCachedUsers() throws -> [User]
func saveUsers(_: [User]) throws
}
final class UserRepositoryImpl: UserRepository {
private let remote: UserRemoteDataSource
private let local: UserLocalDataSource
init(remote: UserRemoteDataSource, local: UserLocalDataSource) {
self.remote = remote
self.local = local
}
func getUsers() async throws -> [User] {
if let cached = try? local.getCachedUsers() {
return cached
}
let users = try await remote.fetchUsers()
try local.saveUsers(users)
return users
}
}
Dependency Injection in iOS für Repository wird typischerweise über eine Factory oder einen DI-Container (Swinject, Factory) konfiguriert. In Tests wird das UserRepository-Protokoll durch eine Mock-Implementierung ersetzt, die vordefinierte Daten zurückgibt. Async-await macht den Code synchron und lesbar ohne Closures und Delegates. Für Combine-Reaktivität geben Repository-Methoden AnyPublisher statt async throws zurück.
Android-Implementierung des Repository verwendet umfassend Kotlin Coroutines und Flow für asynchrone Operationen. Google empfiehlt Repository im offiziellen Android-Architekturleitfaden (Android Architecture Components). Das Repository akzeptiert RemoteDataSource (Retrofit) und LocalDataSource (Room) über den Konstruktor, und das ViewModel abonniert einen Flow vom Repository. Das Repository verwaltet die Datenstrategie: Cache-First, Network-First oder immer Netzwerk mit Cache-Schreiben.
interface UserRepository {
fun getUsers(): Flow<Result<List<User>>>
}
interface UserRemoteDataSource {
suspend fun fetchUsers(): List<User>
}
interface UserLocalDataSource {
fun getCachedUsers(): Flow<List<User>>
suspend fun saveUsers(users: List<User>)
}
class UserRepositoryImpl(
private val remote: UserRemoteDataSource,
private val local: UserLocalDataSource
) : UserRepository {
override fun getUsers(): Flow<Result<List<User>>> = flow {
emit(Result.Loading)
local.getCachedUsers().collect { cached ->
if (cached.isNotEmpty()) {
emit(Result.Success(cached))
}
}
try {
val users = remote.fetchUsers()
local.saveUsers(users)
emit(Result.Success(users))
} catch (e: Exception) {
emit(Result.Error(e))
}
}
}
Result-Wrapper im obigen Beispiel ist Standard für Android: Eine versiegelte Klasse Result informiert das ViewModel über den Ladezustand (Loading, Success, Error). Das ViewModel abonniert über collect und aktualisiert StateFlow oder LiveData. Repository mit Flow benachrichtigt die UI automatisch über Datenbankänderungen — dies ist ein wichtiger Unterschied zu einmaligen Anfragen, bei denen die UI ohne manuelle Aktualisierung nichts von Änderungen erfährt.
DataSource — Klassen, die für die Arbeit mit einer bestimmten Datenquelle verantwortlich sind. RemoteDataSource verwendet einen HTTP-Client (URLSession, Retrofit, Ktor), um Daten von der API abzurufen. LocalDataSource arbeitet mit lokalem Speicher (CoreData, Realm, Room, UserDefaults, DataStore). Jede DataSource hat eine enge Verantwortung: RemoteDataSource kennt nur das API-Anfrageformat, LocalDataSource — das Datenbankschema. Das Repository kombiniert sie und implementiert eine Caching-Strategie.
| DataSource | iOS-Plattform | Android-Plattform | Quelle |
|---|---|---|---|
| Remote | URLSession + Codable | Retrofit + Moshi/Gson | REST / GraphQL API |
| Local (DB) | CoreData, SwiftData | Room, SQLDelight | SQLite auf Gerät |
| Local (Cache) | NSCache, UserDefaults | DataStore, EncryptedSP | In-Memory / Disk |
| Einstellungen | UserDefaults, Keychain | SharedPreferences, EncryptedSP | Einstellungen, Token |
Caching-Strategien im Repository: Cache-First (zuerst Cache, dann Hintergrundladen), Network-Only (nur Netzwerk, für Zahlungsbildschirme), Network-First-With-Cache-Backup (zuerst Netzwerk, bei Fehler Fallback auf Cache). Die Wahl der Strategie hängt vom Szenario ab: Eine Länderliste kann lange gecacht werden, Wechselkurse — für 15 Minuten, Wallet-Guthaben — nur aus dem Netzwerk. Das Repository implementiert die Strategie und ändert sie, ohne ViewModel oder UI zu modifizieren.
Repository und Service sind unterschiedliche Muster mit überlappenden Funktionen. Repository ist für Datenzugriff und Caching verantwortlich und gibt Datenmodelle zurück. Service (oder Interactor, Use Case) enthält Geschäftslogik: Validierung, Datentransformation, Orchestrierung mehrerer Repository-Aufrufe. Service kann UserRepository, OrderRepository und NotificationRepository kombinieren, um eine Bestellung zu verarbeiten. Repository enthält keine Geschäftslogik — nur CRUD und Caching.
Wann Repository wählen — Datennavigation mit mehreren Quellen (API + DB + Cache), Offline-First-Architektur, Notwendigkeit von Caching und transparentem Quellenwechsel. Repository ist in Clean Architecture obligatorisch und wird von Google für Android-Anwendungen empfohlen. In der VIPER-Architektur unter iOS übernimmt die Interactor-Schicht die Rolle des Repository und interagiert mit Manager oder Service für den Datenzugriff.
Wann Service ausreicht — einfache Anwendungen mit einer einzigen Datenquelle, Nur-Lese-Bildschirme ohne Schreibzugriff, Projekte ohne Offline-Modus. In solchen Fällen wird DataSource direkt von ViewModel oder Presenter verwendet, und Repository wird zu einer überflüssigen Schicht. Das frühzeitige Hinzufügen von Repository erfordert jedoch keinen großen Aufwand und vereinfacht das spätere Hinzufügen von Caching und Tests.
Häufig gestellte Fragen
DataSource ist eine Klasse, die mit einer einzigen Quelle arbeitet (API, DB, Cache). Repository ist eine Klasse, die mehrere DataSources verwaltet und eine einheitliche Schnittstelle bereitstellt. Das Repository entscheidet, welche DataSource verwendet wird, und koordiniert das Caching. DataSource weiß nichts von der Existenz anderer Quellen; Repository kennt keine Implementierungsdetails jeder Quelle.
Ja, Repository ist in SwiftUI nützlich, um Daten von der View zu trennen. Das ViewModel abonniert einen Publisher aus dem Repository, und das Repository verwaltet Caching und Synchronisation. In einfachen Anwendungen kann URLSession direkt im ViewModel verwendet werden, aber für Testbarkeit und Skalierbarkeit ist Repository vorzuziehen. Apple erzwingt das Muster nicht, aber es ist kompatibel mit SwiftData und Network.framework.
DataSources werden über Dependency Injection durch Mock-Objekte ersetzt. Der Test erstellt eine Mock-RemoteDataSource (gibt vordefiniertes JSON zurück) und eine Mock-LocalDataSource (prüft, ob Daten gespeichert wurden). Das Repository wird isoliert getestet: Caching-Strategie, Fehlerbehandlung und korrekte Aufrufreihenfolge werden überprüft. Für Integrationstests werden TestDispatcher (Kotlin) oder MainActor.run (Swift) verwendet.
Möglich, aber nicht empfohlen. Ohne Protokoll ist es unmöglich, die Implementierung in Tests und Vorschauen zu ersetzen. In Kotlin ermöglicht das Repository-Interface das Ersetzen der Implementierung über DI (Dagger, Hilt, Koin). In Swift ist das Repository-Protokoll für das Testen von async-await- und Combine-Code obligatorisch. Ausnahme sind einfache Projekte mit einer einzigen Datenquelle, bei denen Repository keine Caching-Logik enthält.
Offline-First ist eine Strategie, bei der die Anwendung ohne Internet mit lokalen Daten arbeitet. Repository spielt eine Schlüsselrolle: Es gibt zuerst Daten aus der lokalen DataSource zurück und synchronisiert dann im Hintergrund mit dem Server. Der Benutzer sieht die Daten sofort, und das Repository aktualisiert sie nach dem Laden aus dem Netzwerk. Room mit Flow bietet reaktive UI-Updates bei Datenänderungen in der lokalen Datenbank.
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