Repository Pattern — biznes məntiqi ilə məlumat mənbələri arasında abstraksiya qatı əlavə edən nümunədir. Birbaşa API, verilənlər bazası və ya keş çağırışları əvəzinə Repository məlumatların alınması və saxlanması üçün vahid interfeys təqdim edir. Bu, test etməni və mənbələr arasında keçidi asanlaşdırır. Ətraflı Android Data Layer sənədlərində.
Əsas məqamlar
Repository Pattern — biznes məntiqini məlumat mənbələrinə birbaşa girişdən təcrid edən struktur nümunəsidir. Activity, UIViewController və ya ViewModel birbaşa Retrofit, URLSession, Room və ya CoreData-nı çağırmaq əvəzinə Repository-yə müraciət edir. Repository məlumatların haradan gətiriləcəyinə qərar verir: şəbəkədən, verilənlər bazasından və ya keşdən və nəticəni vahid formatda qaytarır. Bu, tək məsuliyyət prinsipinin tətbiqidir — UI məlumatların necə və haradan alındığını bilmir.
Repository komponentləri interfeys (protocol), tətbiq və bir və ya daha çox DataSource daxildir. DataSource — bir mənbə ilə işləyən sinif: RemoteDataSource HTTP-klient vasitəsilə API-ni çağırır, LocalDataSource verilənlər bazasına oxuyur və yazır. Repository DataSource-ları konstruktor vasitəsilə qəbul edir (Dependency Injection) və hansı mənbəyə müraciət edəcəyini seçir. Məsələn, istifadəçi siyahısı sorğusunda Repository əvvəlcə keşi, sonra verilənlər bazasını, daha sonra şəbəkəni yoxlayır.
Üstünlükləri Repository Pattern: məlumat mənbələrindəki dəyişikliklər (API dəyişikliyi, VB miqrasiyası) UI qatına təsir etmir; Repository və ya DataSource-un dəyişdirilməsi ilə unit test etmə; UI üçün şəffaf keşləmə; ekran məntiqini dəyişmədən online və offline rejimləri arasında keçid. Android icması Repository-ni Clean Architecture-da məcburi qat kimi tövsiyə edir.
iOS tətbiqi Repository Swift protokollarına əsaslanır. Repository protokolu məlumatların alınması və saxlanması üçün metodlar elan edir. Həqiqi tətbiq initiator vasitəsilə daxil edilir — bu, testlərdə və SwiftUI preview-də tətbiqi dəyişməyə imkan verir. DataSource-lar da protokollarla elan edilir: Protocol RemoteDataSource, Protocol LocalDataSource. ViewModel və ya Interactor konkret tətbiqi bilmir — yalnız Repository protokolunu bilir.
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 iOS-da Repository üçün adətən fabrik və ya DI konteyneri (Swinject, Factory) vasitəsilə konfiqurasiya olunur. Testlərdə UserRepository protokolu əvvəlcədən təyin edilmiş məlumatları qaytaran mock tətbiqi ilə əvəz olunur. Async-await kodu bağlanmalar və delegate-lər olmadan sinxron və oxunaqlı edir. Combine reaktivliyi üçün Repository metodları async throws əvəzinə AnyPublisher qaytarır.
Android tətbiqi Repository asinxron iş üçün Kotlin Coroutines və Flow-dan geniş istifadə edir. Google Repository-ni Android memarlığı üzrə rəsmi təlimatda (Android Architecture Components) tövsiyə edir. Repository konstruktor vasitəsilə RemoteDataSource (Retrofit) və LocalDataSource (Room) qəbul edir, ViewModel isə Repository-dən Flow-a abunə olur. Repository məlumat strategiyasını idarə edir: əvvəlcə keş, sonra şəbəkə və ya həmişə şəbəkə keşə yazmaqla.
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 sarğısı yuxarıdakı nümunədə Android üçün standartdır: sealed class Result ViewModel-ə yükləmə vəziyyəti (Loading, Success, Error) barədə məlumat verir. ViewModel collect vasitəsilə abunə olur və StateFlow və ya LiveData-nı yeniləyir. Repository Flow ilə avtomatik olaraq UI-ə verilənlər bazasındakı dəyişikliklər barədə xəbərdarlıq edir — bu, UI-nin əl ilə yeniləmə olmadan dəyişikliklərdən xəbərsiz qaldığı birdəfəlik sorğulardan əsas fərqdir.
DataSource — konkret məlumat mənbəyi ilə işləməyə cavabdeh olan siniflər. RemoteDataSource API-dən məlumat almaq üçün HTTP-klientdən (URLSession, Retrofit, Ktor) istifadə edir. LocalDataSource yerli yaddaşla (CoreData, Realm, Room, UserDefaults, DataStore) işləyir. Hər DataSource dar məsuliyyət daşıyır: RemoteDataSource yalnız API sorğu formatını bilir, LocalDataSource — verilənlər bazası sxemini. Repository onları birləşdirərək keşləmə strategiyasını tətbiq edir.
| DataSource | iOS Platforması | Android Platforması | Mənbə |
|---|---|---|---|
| Remote | URLSession + Codable | Retrofit + Moshi/Gson | REST / GraphQL API |
| Local (VB) | CoreData, SwiftData | Room, SQLDelight | Cihazda SQLite |
| Local (keş) | NSCache, UserDefaults | DataStore, EncryptedSP | Yaddaşdaxili / disk |
| Preference | UserDefaults, Keychain | SharedPreferences, EncryptedSP | Parametrlər, tokenlər |
Keşləmə strategiyaları Repository-də: Cache-First (əvvəlcə keş, sonra fon yükləməsi), Network-Only (yalnız şəbəkə, ödəniş ekranları üçün), Network-First-With-Cache-Backup (əvvəlcə şəbəkə, xəta olduqda — keş). Strategiya seçimi ssenaridən asılıdır: ölkələr siyahısını uzun müddət keşləmək olar, valyuta məzənnələrini — 15 dəqiqə, pulqabı balansını — yalnız şəbəkədən. Repository strategiyanı tətbiq edir və ViewModel və ya UI-ni dəyişmədən dəyişir.
Repository və Service — üst-üstə düşən funksiyaları olan fərqli nümunələr. Repository məlumatlara giriş və onların keşlənməsi üçün cavabdehdir, məlumat modellərini qaytarır. Service (və ya Interactor, Use Case) biznes məntiqini ehtiva edir: validasiya, məlumat transformasiyası, bir neçə Repository çağırışının orkestrasiyası. Service sifarişin rəsmiləşdirilməsi üçün UserRepository, OrderRepository və NotificationRepository-i birləşdirə bilər. Repository biznes məntiqini ehtiva etmir — yalnız CRUD və keşləmə.
Repository nə vaxt seçilməlidir — çoxsaylı mənbələrlə (API + VB + keş) məlumat naviqasiyası, offline-first memarlığı, keşləmə və mənbələrin şəffaf dəyişdirilməsi ehtiyacı. Repository Clean Architecture-da məcburidir və Google tərəfindən Android tətbiqləri üçün tövsiyə olunur. iOS-da VIPER memarlığında Repository rolunu Interactor qatı yerinə yetirir, məlumatlara giriş üçün Manager və ya Service ilə qarşılıqlı əlaqədə olur.
Service nə vaxt kifayətdir — bir məlumat mənbəyi olan sadə tətbiqlər, yazma olmadan yalnız oxuma ekranları, offline rejimi olmayan layihələr. Belə hallarda DataSource birbaşa ViewModel və ya Presenter tərəfindən istifadə olunur, Repository isə artıq qata çevrilir. Bununla belə, Repository-ni erkən mərhələdə əlavə etmək böyük xərc tələb etmir və gələcəkdə keşləmə və testlərin əlavə edilməsini asanlaşdırır.
Tez-tez verilən suallar
DataSource — bir mənbə ilə işləyən sinif (API, VB, keş). Repository — bir neçə DataSource-u idarə edən və vahid interfeys təqdim edən sinif. Repository hansı DataSource-dan məlumat götürəcəyinə qərar verir və keşləməni koordinasiya edir. DataSource digər mənbələrin mövcudluğundan xəbərsizdir, Repository hər bir mənbənin tətbiq detallarını bilmir.
Bəli, Repository SwiftUI-də məlumatları View-dən ayırmaq üçün faydalıdır. ViewModel Repository-dən Publisher-ə abunə olur, Repository isə keşləmə və sinxronizasiyanı idarə edir. Sadə tətbiqlərdə birbaşa ViewModel-də URLSession istifadə etmək olar, lakin test oluna bilmə və miqyaslama üçün Repository daha üstündür. Apple bu nümunəni tətbiq etmir, lakin o SwiftData və Network.framework ilə uyğundur.
DataSource-lar Dependency Injection vasitəsilə mock-obyektlərlə əvəz olunur. Test mock RemoteDataSource (əvvəlcədən təyin edilmiş JSON qaytarır) və mock LocalDataSource (məlumatların saxlandığını yoxlayır) yaradır. Repository təcrid olunmuş şəkildə test edilir: keşləmə strategiyası, xəta idarəetməsi və çağırışların düzgün ardıcıllığı yoxlanılır. İnteqrasiya testləri üçün TestDispatcher (Kotlin) və ya MainActor.run (Swift) istifadə olunur.
Mümkündür, lakin tövsiyə edilmir. Protokol olmadan testlərdə və preview-də tətbiqi dəyişmək mümkün deyil. Kotlin-də Repository interfeysi DI (Dagger, Hilt, Koin) vasitəsilə tətbiqi dəyişməyə imkan verir. Swift-də Repository protokolu async-await və Combine kodunun test edilməsi üçün məcburidir. İstisna — keşləmə məntiqi olmayan, tək məlumat mənbəyi olan sadə layihələrdir.
Offline-first — tətbiqin lokal məlumatlardan istifadə edərək internet olmadan işlədiyi strategiyadır. Repository əsas rol oynayır: əvvəlcə lokal DataSource-dan məlumatları qaytarır, sonra fonda serverlə sinxronlaşır. İstifadəçi məlumatları dərhal görür, Repository isə şəbəkədən yüklədikdən sonra onları yeniləyir. Room Flow ilə lokal verilənlər bazasında dəyişikliklər zamanı UI-nin reaktiv yenilənməsini təmin edir.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun