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 объявляет методы для получения и сохранения данных. Реальная реализация внедряется через инициализатор — это позволяет подменять реализацию в тестах и 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 / disk
PreferenceUserDefaults, 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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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