Repository Pattern: چیست، الگوی انتزاع داده در iOS و Android

نویسنده: IT Sectr منتشر شده: 2026-02-17 زمان مطالعه: 7 دقیقه

Repository Pattern — الگویی که لایه‌ای از انتزاع بین منطق کسب‌وکار و منابع داده اضافه می‌کند. به جای فراخوانی مستقیم API، پایگاه داده یا حافظه پنهان، Repository یک رابط یکپارچه برای دریافت و ذخیره داده فراهم می‌کند. این کار تست‌پذیری و جابجایی بین منابع را ساده می‌کند. جزئیات بیشتر در مستندات Android Data Layer.

نکات اصلی

  • Repository Pattern — لایه میانی بین منطق کسب‌وکار و منابع داده (API، DB، حافظه پنهان)
  • DataSource — کلاس‌های جداگانه برای هر منبع: RemoteDataSource، LocalDataSource
  • Single source of truth — Repository به منبع یکپارچه داده برای لایه UI تبدیل می‌شود
  • تست‌پذیری — Repository به راحتی از طریق DI با شیء mock برای تست‌های واحد جایگزین می‌شود
  • سازگاری — با 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، مهاجرت DB) بر لایه UI تأثیر نمی‌گذارد؛ تست واحد از طریق جایگزینی 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 به جای async throws AnyPublisher برمی‌گردانند.

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

Wrapper Result در مثال بالا برای Android استاندارد است: sealed class Result وضعیت بارگذاری (Loading, Success, Error) را به ViewModel اطلاع می‌دهد. 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 (DB)CoreData, SwiftDataRoom, SQLDelightSQLite روی دستگاه
Local (حافظه پنهان)NSCache, UserDefaultsDataStore, EncryptedSPدرون حافظه / دیسک
تنظیماتUserDefaults, KeychainSharedPreferences, EncryptedSPتنظیمات، توکن‌ها

استراتژی‌های ذخیره در حافظه پنهان در Repository: Cache-First (ابتدا حافظه پنهان، سپس بارگذاری پس‌زمینه)، Network-Only (فقط شبکه، برای صفحه‌های پرداخت)، Network-First-With-Cache-Backup (ابتدا شبکه، در صورت خطا — حافظه پنهان). انتخاب استراتژی به سناریو بستگی دارد: لیست کشورها را می‌توان طولانی مدت ذخیره کرد، نرخ ارز — ۱۵ دقیقه، موجودی کیف پول — فقط از شبکه. Repository استراتژی را پیاده‌سازی می‌کند و بدون تغییر ViewModel یا UI آن را تغییر می‌دهد.

Repository Pattern در مقابل Service Layer: تفاوت‌ها و انتخاب

Repository و Service — الگوهای متفاوتی با عملکردهای همپوشان. Repository مسئول دسترسی به داده و ذخیره آن در حافظه پنهان است و مدل‌های داده را برمی‌گرداند. Service (یا Interactor, Use Case) شامل منطق کسب‌وکار است: اعتبارسنجی، تبدیل داده، هماهنگی فراخوانی چندین Repository. Service می‌تواند UserRepository، OrderRepository و NotificationRepository را برای ثبت سفارش ترکیب کند. Repository حاوی منطق کسب‌وکار نیست — فقط CRUD و ذخیره در حافظه پنهان.

چه زمانی Repository را انتخاب کنیم — پیمایش داده با منابع متعدد (API + DB + حافظه پنهان)، معماری offline-first، نیاز به ذخیره در حافظه پنهان و جابجایی شفاف بین منابع. Repository در Clean Architecture اجباری است و توسط Google برای برنامه‌های Android توصیه می‌شود. در معماری VIPER در iOS، نقش Repository توسط لایه Interactor انجام می‌شود که برای دسترسی به داده با Manager یا Service تعامل دارد.

چه زمانی Service کافی است — برنامه‌های ساده با یک منبع داده، صفحه‌های فقط خواندنی بدون نوشتن، پروژه‌های بدون حالت آفلاین. در این موارد، DataSource مستقیماً توسط ViewModel یا Presenter استفاده می‌شود و Repository یک لایه اضافی می‌شود. با این حال، افزودن Repository در مراحل اولیه هزینه زیادی ندارد و افزودن ذخیره در حافظه پنهان و تست‌ها را در آینده ساده‌تر می‌کند.

سوالات متداول

تفاوت Repository با DataSource چیست؟

DataSource — کلاسی که با یک منبع کار می‌کند (API، DB، حافظه پنهان). 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‌ها از طریق Dependency Injection با اشیاء mock جایگزین می‌شوند. تست یک RemoteDataSource mock (که JSON از پیش تعیین‌شده برمی‌گرداند) و یک LocalDataSource mock (که بررسی می‌کند داده ذخیره شده است) ایجاد می‌کند. 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، DB و حافظه پنهان
  • پروتکل‌ها — برای تست و جایگزینی پیاده‌سازی ضروری هستند
  • استراتژی‌های ذخیره در حافظه پنهان — Cache-First، Network-Only، Network-First-With-Cache-Backup
  • iOS — async-await یا Combine با پروتکل‌ها
  • Android — Kotlin Flow + Room + Retrofit، رویکرد توصیه شده توسط Google
  • تست‌پذیری — mock DataSource از طریق DI، بررسی استراتژی‌های ذخیره در حافظه پنهان

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید