Repository Pattern: какво е, модел за абстракция на данни в iOS и Android

Автор: IT Sectr Публикувано: 2026-02-17 Време за четене: 7 мин

Repository Pattern — модел, който добавя слой на абстракция между бизнес логиката и източниците на данни. Вместо директни извиквания на API, база данни или кеш, Repository предоставя единен интерфейс за получаване и запазване на данни. Това опростява тестването и превключването между източници. Повече в документацията Android Data Layer.

Основни точки

  • Repository Pattern — междинен слой между бизнес логиката и източниците на данни (API, БД, кеш)
  • DataSource — отделни класове за всеки източник: RemoteDataSource, LocalDataSource
  • Single source of truth — Repository се превръща в единен източник на данни за UI слоя
  • Тестване — Repository лесно се заменя с mock обект чрез DI за unit тестове
  • Съвместимост — работи с MVVM, Clean Architecture и други архитектурни модели

Какво е Repository Pattern в мобилната разработка?

Repository Pattern — структурен модел, който изолира бизнес логиката от директния достъп до източниците на данни. Вместо Activity, UIViewController или ViewModel директно да извикват Retrofit, URLSession, Room или CoreData, те се обръщат към Repository. Repository решава откъде да вземе данни: от мрежата, базата данни или кеша, и връща резултата в единен формат. Това е имплементация на принципа за единна отговорност — UI не знае как и откъде са получени данните.

Компоненти на Repository включват интерфейс (protocol), имплементация и един или повече DataSource. DataSource — клас, който работи с един източник: RemoteDataSource извиква API чрез HTTP клиент, LocalDataSource чете и пише в базата данни. Repository приема DataSource чрез конструктора (Dependency Injection) и избира към кой източник да се обърне. Например, при заявка за списък с потребители, Repository първо проверява кеша, след това базата данни, после мрежата.

Предимства на Repository Pattern: изолиране на промените в източниците на данни (промяна на API, миграция на БД) не засяга UI слоя; unit тестване чрез замяна на Repository или DataSource; кеширане прозрачно за UI; превключване между онлайн и офлайн режим без промяна на логиката на екрана. Общността на Android препоръчва Repository като задължителен слой в Clean Architecture.

Repository Pattern в iOS със Swift: имплементация и пример

iOS имплементация на Repository се основава на Swift протоколи. Протоколът Repository декларира методи за получаване и запазване на данни. Реалната имплементация се инжектира чрез инициализатора — това позволява замяна на имплементацията в тестове и SwiftUI прегледи. DataSource също се декларират с протоколи: Protocol RemoteDataSource, Protocol LocalDataSource. ViewModel или Interactor не знае конкретната имплементация — знае само протокола 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 в iOS за Repository обикновено се конфигурира чрез фабрика или DI контейнер (Swinject, Factory). В тестовете протоколът UserRepository се заменя с mock имплементация, която връща предварително дефинирани данни. Async-await прави кода синхронен и четим без затваряния и делегати. За реактивност на Combine, методите на Repository връщат AnyPublisher вместо async throws.

Repository Pattern в Android с Kotlin: пример с Flow

Android имплементация на Repository широко използва Kotlin Coroutines и Flow за асинхронна работа. Google препоръчва Repository в официалното ръководство за Android архитектура (Android Architecture Components). Repository приема RemoteDataSource (Retrofit) и LocalDataSource (Room) чрез конструктора, а ViewModel се абонира за Flow от Repository. Repository управлява стратегията за данни: първо кеш, после мрежа или винаги мрежа със запис в кеш.

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

Result обвивка в примера по-горе е стандартна за Android: sealed class Result уведомява ViewModel за състоянието на зареждане (Loading, Success, Error). ViewModel се абонира чрез collect и актуализира StateFlow или LiveData. Repository с Flow автоматично уведомява UI за промени в базата данни — това е ключовата разлика от еднократните заявки, при които UI не научава за промени без ръчно опресняване.

DataSource: Remote, Local и кеширане на данни

DataSource — класове, отговорни за работа с конкретен източник на данни. RemoteDataSource използва HTTP клиент (URLSession, Retrofit, Ktor) за получаване на данни от API. LocalDataSource работи с локално хранилище (CoreData, Realm, Room, UserDefaults, DataStore). Всеки DataSource има тясна отговорност: RemoteDataSource знае само формата на API заявката, LocalDataSource — схемата на базата данни. Repository ги комбинира, имплементирайки стратегия за кеширане.

DataSourceПлатформа iOSПлатформа AndroidИзточник
RemoteURLSession + CodableRetrofit + Moshi/GsonREST / GraphQL API
Local (БД)CoreData, SwiftDataRoom, SQLDelightSQLite на устройството
Local (кеш)NSCache, UserDefaultsDataStore, EncryptedSPВ паметта / диск
ПредпочитанияUserDefaults, KeychainSharedPreferences, EncryptedSPНастройки, токени

Стратегии за кеширане в Repository: Cache-First (първо кеш, след това фоново зареждане), Network-Only (само мрежа, за екрани за плащане), Network-First-With-Cache-Backup (първо мрежа, при грешка — кеш). Изборът на стратегия зависи от сценария: списък с държави може да се кешира дълго, валутни курсове — 15 минути, баланс на портфейла — само от мрежата. Repository имплементира стратегията и я променя без модификация на ViewModel или UI.

Repository Pattern vs Service Layer: разлики и избор

Repository и Service — различни модели с припокриващи се функции. Repository отговаря за достъпа до данни и тяхното кеширане, връщайки модели на данни. Service (или Interactor, Use Case) съдържа бизнес логика: валидация, трансформация на данни, оркестриране на извиквания към множество Repository. Service може да комбинира UserRepository, OrderRepository и NotificationRepository за обработка на поръчка. Repository не съдържа бизнес логика — само CRUD и кеширане.

Кога да изберете Repository — навигация на данни с множество източници (API + БД + кеш), offline-first архитектура, нужда от кеширане и прозрачно превключване на източници. Repository е задължително в Clean Architecture и се препоръчва от Google за Android приложения. В архитектурата VIPER на iOS, ролята на Repository се изпълнява от Interactor слоя, който взаимодейства с Manager или Service за достъп до данни.

Кога Service е достатъчен — прости приложения с един източник на данни, екрани само за четене без запис, проекти без офлайн режим. В такива случаи DataSource се използва директно от ViewModel или Presenter, а Repository става излишен слой. Въпреки това, добавянето на Repository в ранен етап не изисква големи разходи и улеснява добавянето на кеширане и тестове в бъдеще.

Често задавани въпроси

Как се различава Repository от DataSource?

DataSource — клас, който работи с един източник (API, БД, кеш). Repository — клас, който управлява множество DataSource и предоставя единен интерфейс. Repository решава от кой DataSource да вземе данни и координира кеширането. DataSource не знае за съществуването на други източници, Repository не знае детайлите на имплементацията на всеки източник.

Необходим ли е Repository в iOS със SwiftUI?

Да, Repository е полезен в SwiftUI за отделяне на данните от View. ViewModel се абонира за Publisher от Repository, а Repository управлява кеширането и синхронизацията. В прости приложения може да се използва URLSession директно в ViewModel, но за тестване и мащабиране Repository е за предпочитане. Apple не налага този модел, но той е съвместим със SwiftData и Network.framework.

Как да тестваме Repository с множество DataSource?

DataSource се заменят с mock обекти чрез Dependency Injection. Тестът създава mock RemoteDataSource (връща предварително дефиниран JSON) и mock LocalDataSource (проверява дали данните са запазени). Repository се тества изолирано: проверява се стратегията за кеширане, обработката на грешки и правилния ред на извикванията. За интеграционни тестове се използва TestDispatcher (Kotlin) или MainActor.run (Swift).

Може ли Repository да се използва без интерфейс (protocol)?

Може, но не се препоръчва. Без протокол не може да се замени имплементацията в тестове и прегледи. В Kotlin, интерфейсът Repository позволява замяна на имплементация чрез DI (Dagger, Hilt, Koin). В Swift, протоколът Repository е задължителен за тестване на async-await и Combine код. Изключение — прости проекти с един източник на данни, където Repository не съдържа логика за кеширане.

Какво е offline-first в контекста на Repository?

Offline-first — стратегия, при която приложението работи без интернет, използвайки локални данни. Repository играе ключова роля: първо връща данни от локалния DataSource, след това се синхронизира със сървъра на заден план. Потребителят вижда данните незабавно, а Repository ги актуализира след зареждане от мрежата. Room с Flow осигурява реактивно актуализиране на UI при промяна на данни в локалната база.

Обобщение

  • Repository Pattern — слой на абстракция между UI и източниците на данни
  • DataSource — отделни класове за API, БД и кеш
  • Протоколи — задължителни за тестване и замяна на имплементации
  • Стратегии за кеширане — Cache-First, Network-Only, Network-First-With-Cache-Backup
  • iOS — async-await или Combine с протоколи
  • Android — Kotlin Flow + Room + Retrofit, подход, препоръчан от Google
  • Тестване — mock DataSource чрез DI, проверка на стратегии за кеширане

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също