Repository Pattern: що це, патерн абстракції даних в iOS та Android

Автор: IT Sectr Опубліковано: 2026-02-17 Час читання: 7 хв

Repository Pattern — патерн, який додає шар абстракції між бізнес-логікою та джерелами даних. Замість прямих викликів API, бази даних або кеша Repository надає єдиний інтерфейс для отримання та збереження даних. Це спрощує тестування та перемикання між джерелами. Детальніше — у документації Android Data Layer.

Головне

  • Repository Pattern — прошарок між бізнес-логікою та джерелами даних (API, БД, кеш)
  • DataSource — окремі класи для кожного джерела: RemoteDataSource, LocalDataSource
  • Єдине джерело правди — 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 оголошує методи для отримання та збереження даних. Реальна реалізація впроваджується через ініціалізатор — це дозволяє підміняти реалізацію в тестах та preview 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, EncryptedSPIn-memory / диск
Налаштування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 — прості додатки з одним джерелом даних, read-only екрани без запису, проекти без офлайн-режиму. У таких випадках 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)?

Можна, але не рекомендується. Без протоколу неможливо підмінити реалізацію в тестах та preview. У 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також