Repository Pattern: ce este, modelul de abstractizare a datelor în iOS și Android

Autor: IT Sectr Publicat: 2026-02-17 Timp de citire: 7 min

Repository Pattern — un model care adaugă un strat de abstractizare între logica de afaceri și sursele de date. În locul apelurilor directe API, bază de date sau cache, Repository oferă o interfață unică pentru obținerea și salvarea datelor. Acest lucru simplifică testarea și comutarea între surse. Mai multe în documentația Android Data Layer.

Esential

  • Repository Pattern — strat intermediar între logica de afaceri și sursele de date (API, BD, cache)
  • DataSource — clase separate pentru fiecare sursă: RemoteDataSource, LocalDataSource
  • Single source of truth — Repository devine sursa unică de date pentru stratul UI
  • Testare — Repository se înlocuiește ușor cu un obiect mock prin DI pentru teste unitare
  • Compatibilitate — funcționează cu MVVM, Clean Architecture și alte modele arhitecturale

Ce este Repository Pattern în dezvoltarea mobilă?

Repository Pattern — un model structural care izolează logica de afaceri de accesul direct la sursele de date. În loc ca Activity, UIViewController sau ViewModel să apeleze direct Retrofit, URLSession, Room sau CoreData, acestea se adresează Repository-ului. Repository decide de unde să preia datele: din rețea, bază de date sau cache, și returnează rezultatul într-un format unic. Aceasta este o implementare a principiului responsabilității unice — UI nu știe cum și de unde au fost obținute datele.

Componentele Repository includ interfața (protocol), implementarea și unul sau mai multe DataSource-uri. DataSource — o clasă care lucrează cu o singură sursă: RemoteDataSource apelează API prin client HTTP, LocalDataSource citește și scrie în baza de date. Repository primește DataSource-urile prin constructor (Dependency Injection) și decide la ce sursă să se adreseze. De exemplu, la cererea listei de utilizatori, Repository verifică mai întâi cache-ul, apoi baza de date, apoi rețeaua.

Avantajele Repository Pattern: izolarea modificărilor surselor de date (schimbarea API, migrarea BD) nu afectează stratul UI; testarea unitară prin înlocuirea Repository sau DataSource; stocarea în cache transparentă pentru UI; comutarea între modurile online și offline fără modificarea logicii ecranului. Comunitatea Android recomandă Repository ca strat obligatoriu în Clean Architecture.

Repository Pattern în iOS cu Swift: implementare și exemplu

Implementarea iOS a Repository se bazează pe protocoale Swift. Protocolul Repository declară metodele de obținere și salvare a datelor. Implementarea reală este injectată prin inițializator — aceasta permite înlocuirea implementării în teste și în preview SwiftUI. DataSource-urile sunt de asemenea declarate prin protocoale: Protocol RemoteDataSource, Protocol LocalDataSource. ViewModel sau Interactor nu cunoaște implementarea concretă — cunoaște doar protocolul Repository.

swift
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 în iOS pentru Repository se configurează de obicei printr-o fabrică sau container DI (Swinject, Factory). În teste, protocolul UserRepository este înlocuit cu o implementare mock care returnează date predefinite. Async-await face codul sincron și lizibil fără închideri și delegări. Pentru reactivitatea Combine, metodele Repository returnează AnyPublisher în loc de async throws.

Repository Pattern în Android cu Kotlin: exemplu cu Flow

Implementarea Android a Repository utilizează pe scară largă Kotlin Coroutines și Flow pentru lucrul asincron. Google recomandă Repository în ghidul oficial de arhitectură Android (Android Architecture Components). Repository primește RemoteDataSource (Retrofit) și LocalDataSource (Room) prin constructor, iar ViewModel se abonează la Flow din Repository. Repository gestionează strategia datelor: mai întâi cache, apoi rețea sau întotdeauna rețea cu scriere în cache.

kotlin
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))
        }
    }
}

Înfășurătoarea Result în exemplul de mai sus este standardă pentru Android: sealed class Result informează ViewModel despre starea încărcării (Loading, Success, Error). ViewModel se abonează prin collect și actualizează StateFlow sau LiveData. Repository cu Flow notifică automat UI despre modificările din baza de date — aceasta este diferența cheie față de cererile unice, unde UI nu află despre modificări fără reîmprospătare manuală.

DataSource: Remote, Local și stocarea în cache a datelor

DataSource — clase responsabile pentru lucrul cu o anumită sursă de date. RemoteDataSource folosește un client HTTP (URLSession, Retrofit, Ktor) pentru a obține date din API. LocalDataSource lucrează cu stocarea locală (CoreData, Realm, Room, UserDefaults, DataStore). Fiecare DataSource are o responsabilitate restrânsă: RemoteDataSource știe doar formatul cererii API, LocalDataSource — schema bazei de date. Repository le combină, implementând strategia de stocare în cache.

DataSourcePlatforma iOSPlatforma AndroidSursă
RemoteURLSession + CodableRetrofit + Moshi/GsonREST / GraphQL API
Local (BD)CoreData, SwiftDataRoom, SQLDelightSQLite pe dispozitiv
Local (cache)NSCache, UserDefaultsDataStore, EncryptedSPIn-memory / disc
PreferințeUserDefaults, KeychainSharedPreferences, EncryptedSPSetări, tokenuri

Strategii de stocare în cache în Repository: Cache-First (mai întâi cache, apoi încărcare în fundal), Network-Only (doar rețea, pentru ecrane de plată), Network-First-With-Cache-Backup (mai întâi rețea, la eroare — cache). Alegerea strategiei depinde de scenariu: lista țărilor poate fi stocată în cache pentru mult timp, cursurile valutare — 15 minute, soldul portofelului — doar din rețea. Repository implementează strategia și o modifică fără a schimba ViewModel sau UI.

Repository Pattern vs Service Layer: diferențe și alegere

Repository și Service — modele diferite cu funcții care se suprapun. Repository răspunde pentru accesul la date și stocarea lor în cache, returnând modele de date. Service (sau Interactor, Use Case) conține logica de afaceri: validare, transformare a datelor, orchestrarea apelurilor mai multor Repository-uri. Service poate combina UserRepository, OrderRepository și NotificationRepository pentru procesarea unei comenzi. Repository nu conține logică de afaceri — doar CRUD și stocare în cache.

Când să alegeți Repository — navigare prin date cu surse multiple (API + BD + cache), arhitectură offline-first, necesitatea stocării în cache și comutării transparente între surse. Repository este obligatoriu în Clean Architecture și recomandat de Google pentru aplicațiile Android. În arhitectura VIPER pe iOS, rolul Repository este îndeplinit de stratul Interactor, care interacționează cu Manager sau Service pentru accesul la date.

Când este suficient Service — aplicații simple cu o singură sursă de date, ecrane doar pentru citire fără scriere, proiecte fără mod offline. În astfel de cazuri, DataSource este utilizat direct de ViewModel sau Presenter, iar Repository devine un strat redundant. Cu toate acestea, adăugarea Repository într-un stadiu incipient nu necesită costuri mari și simplifică adăugarea stocării în cache și a testelor în viitor.

Întrebări frecvente

Cu ce diferă Repository de DataSource?

DataSource — o clasă care lucrează cu o singură sursă (API, BD, cache). Repository — o clasă care gestionează mai multe DataSource-uri și oferă o interfață unică. Repository decide de la ce DataSource să preia datele și coordonează stocarea în cache. DataSource nu știe de existența altor surse, Repository nu cunoaște detaliile de implementare ale fiecărei surse.

Este necesar Repository în iOS cu SwiftUI?

Da, Repository este util în SwiftUI pentru separarea datelor de View. ViewModel se abonează la Publisher din Repository, iar Repository gestionează stocarea în cache și sincronizarea. În aplicațiile simple se poate utiliza URLSession direct în ViewModel, dar pentru testabilitate și scalabilitate Repository este preferabil. Apple nu impune acest model, dar este compatibil cu SwiftData și Network.framework.

Cum se testează Repository cu mai multe DataSource-uri?

DataSource-urile sunt înlocuite cu obiecte mock prin Dependency Injection. Testul creează un RemoteDataSource mock (care returnează JSON predefinit) și un LocalDataSource mock (care verifică dacă datele au fost salvate). Repository este testat izolat: se verifică strategia de stocare în cache, gestionarea erorilor și ordinea corectă a apelurilor. Pentru teste de integrare se utilizează TestDispatcher (Kotlin) sau MainActor.run (Swift).

Se poate folosi Repository fără interfață (protocol)?

Se poate, dar nu este recomandat. Fără protocol nu se poate înlocui implementarea în teste și la preview. În Kotlin, interfața Repository permite înlocuirea implementării prin DI (Dagger, Hilt, Koin). În Swift, protocolul Repository este obligatoriu pentru testarea codului async-await și Combine. Excepție — proiecte simple cu o singură sursă de date, unde Repository nu conține logică de stocare în cache.

Ce este offline-first în contextul Repository?

Offline-first — o strategie în care aplicația funcționează fără internet, utilizând date locale. Repository joacă un rol cheie: mai întâi returnează date din DataSource-ul local, apoi se sincronizează cu serverul în fundal. Utilizatorul vede datele instantaneu, iar Repository le actualizează după încărcarea din rețea. Room cu Flow asigură actualizarea reactivă a UI la modificarea datelor în baza de date locală.

Rezumat

  • Repository Pattern — strat de abstractizare între UI și sursele de date
  • DataSource — clase separate pentru API, BD și cache
  • Protocoale — obligatorii pentru testare și înlocuirea implementărilor
  • Strategii de stocare în cache — Cache-First, Network-Only, Network-First-With-Cache-Backup
  • iOS — async-await sau Combine cu protocoale
  • Android — Kotlin Flow + Room + Retrofit, abordare recomandată de Google
  • Testare — mock DataSource prin DI, verificarea strategiilor de cache

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și