---
title: 'Depo Kalıbı'
source: 'https://academia.sh/tr/kurslar/android-mimari/depo-kalibi'
course: 'Mimari Bileşenler ve Veri Katmanı'
language: tr
updated: '2026-10-21T05:02:37+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Depo kalıbının çağıran kodu veri kaynaklarından ayırdığını açıklar'
    - 'Yerel ve uzak veri kaynaklarının depo içinde bir sıra oluşturduğunu gösterir'
    - 'Önce belleğe bakan bir depoda yanıtın kökeninin bellek olduğunu ölçer'
    - 'Sıra değişince aynı değerin kökeninin değiştiğini, değerin değişmeyebileceğini gösterir'
---

# Depo Kalıbı

Depo kalıbı, verinin nereden geldiğini çağıran koddan saklar: yerel ve uzak veri kaynaklarını tek bir arayüzün arkasında sıralar. Ders sıralı bir okuma zincirinde yanıtın kökenini ve sıra değişince kökenin nasıl değiştiğini ölçer.

Önceki derste bir sınıfın bağımlılığını kendisi kurmak yerine dışarıdan aldığını gördük. Alan katmanının dışarıdan aldığı bağımlılık çoğu zaman tek bir nesnedir ve veri katmanı onu verir. Bu nesneye depo (repository) denir. Katman Modeli dersinde bir okumanın yanıtının dört kökenden gelebildiğini ölçmüştük: bellek, disk, ağ ya da varsayılan. Bu derste o ölçü yeni bir yere bakıyor: yanıtı veren zinciri kim kuruyor ve çağıran bunu biliyor mu? Dersin sorusu önceki derslerle aynıdır: **bu okumanın yanıtı nereden geliyor?**

Aşağıdaki çekirdek, kursun tüm derslerinde aynı kalır. Bir okuma, basamakların sıralı bir zincirini gezer ve anahtarı taşıyan ilk basamağın kökenini alır. Hiçbir basamak anahtarı taşımıyorsa yanıt varsayılandır.

```kotlin
// Origin.kt — bir okumanin yanitinin hangi basamaktan geldigini siniflandirir
class Tier(val origin: String, private val entries: Map<String, String>) {
    init {
        require(origin in listOf("bellek", "disk", "ag")) { "gecersiz basamak: $origin" }
    }

    fun lookup(key: String): String? = entries[key]
}

class Read(val label: String, val key: String, chain: List<Tier>, fallback: String) {
    private val hit: Tier? = chain.firstOrNull { it.lookup(key) != null }
    val value: String = hit?.lookup(key) ?: fallback
    val origin: String = hit?.origin ?: "varsayilan"

    fun show() {
        println("$label: anahtar=$key | deger=$value -> $origin")
    }
}

fun countOrigins(reads: List<Read>) {
    for (name in listOf("bellek", "disk", "ag", "varsayilan")) {
        println("$name: ${reads.count { it.origin == name }}")
    }
}
```

## Veri Kaynağı ve Depo

Uygulamanın verisi birden çok yerde durur. Disk üzerindeki kayıt yerel veri kaynağıdır (data source), sunucudaki kayıt uzak veri kaynağıdır. Her biri kendi dilini konuşur: biri satır ve sütun, öteki istek ve yanıt. Çağıran kod bu dilleri bilirse, kaynak her değiştiğinde çağıran da değişir. Depo kalıbı (repository pattern) bunu keser: depo, kaynakları kendi içinde bir sıraya dizer ve dışarıya tek bir okuma işlemi sunar.

Platformda bu, bir arayüz ve onu kullanan bir sınıf olarak yazılır. Sınıf yerel kaynağa bakar, bulamazsa uzak kaynağa gider ve gelen yanıtı yerele yazar.

```kotlin
// Platform kodu: ProductRepository
interface ProductLocalDataSource {
    suspend fun price(id: String): String?
    suspend fun save(id: String, price: String)
}

interface ProductRemoteDataSource {
    suspend fun fetchPrice(id: String): String
}

class ProductRepository(
    private val local: ProductLocalDataSource,
    private val remote: ProductRemoteDataSource,
) {
    suspend fun price(id: String): String =
        local.price(id) ?: remote.fetchPrice(id).also { local.save(id, it) }
}
```

`price` çağıran için tek bir askıya alınan fonksiyondur (suspend function). Fiyatın yerelden mi uzaktan mı geldiği çağıranın görmediği bir ayrıntıdır. Bu parça platform kodudur ve burada çıktı, süre ya da kaynak sayısı iddia etmiyoruz. Model, platformun bu kuralını kurar; model platformun kendisi değildir.

Modelde veri kaynağı bir `Tier`'dır ve depo, `Tier` listesini tutan küçük bir sınıftır. Çağıran kod yalnızca depoyu tanır.

```kotlin
// Repository.kt — depo, basamaklari bir sirayla dizer ve tek bir okuma sunar
class Repository(private val chain: List<Tier>) {
    fun read(label: String, key: String, fallback: String): Read = Read(label, key, chain, fallback)
}
```

Bellek basamağını da kendi başına yazmıyoruz. Belleğin içeriği, başka bir kaynaktan çekilmiş anahtarların eşlemidir ve bu çekmeyi bir işlev yapar.

```kotlin
// Sources.kt — bir veri kaynagindan verilen anahtarlarin eslemini cikarir
fun loadFrom(source: Tier, keys: List<String>): Map<String, String> =
    keys.mapNotNull { key -> source.lookup(key)?.let { key to it } }.toMap()
```

Çağıran kod da tek bir yerde durur. Bir ekranın okuduğu beş değeri depodan ister; hangi kaynaktan geldiğini bilmez.

```kotlin
// Caller.kt — cagiran kod depoyu tanir, veri kaynaklarini tanimaz
fun readProfileScreen(repository: Repository): List<Read> = listOf(
    repository.read("profil-adi", "profil.ad", "bilinmiyor"),
    repository.read("urun-fiyati", "urun.fiyat", "bilinmiyor"),
    repository.read("tema", "ayar.tema", "acik"),
    repository.read("stok", "urun.stok", "yok"),
    repository.read("kampanya", "kampanya.kod", "yok"),
)
```

## Önce Belleğe Bakan Zincir

Önce zinciri kuralım. Disk, kayıtlı üç değeri tutar. Sunucu dört değer bilir; fiyatı diskten farklıdır, çünkü sunucuda güncellenmiştir. Bellek basamağı, diskten `profil.ad` ve `urun.fiyat` çekilerek dolduruldu. Depo sırayı bellek, disk, ağ olarak dizer.

```kotlin
// Main.kt — depo okumayi bellek, disk, ag sirasiyla dener
fun main() {
    val disk = Tier("disk", mapOf("profil.ad" to "Ayse", "urun.fiyat" to "120", "ayar.tema" to "koyu"))
    val remote = Tier("ag", mapOf("profil.ad" to "Ayse", "urun.fiyat" to "125", "urun.stok" to "8"))
    val memory = Tier("bellek", loadFrom(disk, listOf("profil.ad", "urun.fiyat")))
    val repository = Repository(listOf(memory, disk, remote))
    val reads = readProfileScreen(repository)
    reads.forEach { it.show() }
    countOrigins(reads)
}
```

```text
profil-adi: anahtar=profil.ad | deger=Ayse -> bellek
urun-fiyati: anahtar=urun.fiyat | deger=120 -> bellek
tema: anahtar=ayar.tema | deger=koyu -> disk
stok: anahtar=urun.stok | deger=8 -> ag
kampanya: anahtar=kampanya.kod | deger=yok -> varsayilan
bellek: 2
disk: 1
ag: 1
varsayilan: 1
```

Beş okumanın beş yanıtı, dört kökene dağıldı. `profil.ad` ve `urun.fiyat` belleğe alındığı için ilk basamakta bulundu. `ayar.tema` bellekte yoktur, diskte vardır. `urun.stok` yalnız sunucuda vardır. `kampanya.kod` hiçbir basamakta yoktur ve çağıranın verdiği yedek değer döner. Bu dersin kökeni bellektir: depoya ilk giren okuma, zincirin başında bulunan basamaktan yanıtlanır.

Dikkat edilecek nokta `urun.fiyat` değeridir: 120. Sunucuda 125 vardır, ama okuma sunucuya hiç ulaşmadı. Kural, sırayı görür, tazeliği görmez. Bellekteki değer bayat olsa da, anahtar orada olduğu sürece zincir ilk eşleşmede durur.

**VK1.** Model, belleğin diskten bir kez dolduğunu ve sonra değişmediğini varsayar. Platformda bellek önbelleğinin boyut sınırı, çıkarma kuralı ve bir değer yazıldığında önbelleğin güncellenmesi modelde yoktur; modelde bellek yalnız bir eşlemdir.

## Çağıran Zincirden Habersizdir

Depo kalıbının değeri, çağıranın zincirden habersiz kalmasıdır. Aynı `readProfileScreen` işlevi iki farklı depoyla çağrılabilir. Birincisi üç basamaklı zincir, ikincisi yalnız ağdan oluşan bir zincirdir: çevrimdışı hazırlanmamış, yerel kaynağı olmayan bir ilk sürüm gibi.

```kotlin
// Main.kt — ayni cagiran kod, iki farkli depo
fun main() {
    val disk = Tier("disk", mapOf("profil.ad" to "Ayse", "urun.fiyat" to "120", "ayar.tema" to "koyu"))
    val remote = Tier("ag", mapOf("profil.ad" to "Ayse", "urun.fiyat" to "125", "urun.stok" to "8"))
    val memory = Tier("bellek", loadFrom(disk, listOf("profil.ad", "urun.fiyat")))
    val chains = listOf(
        "tam" to listOf(memory, disk, remote),
        "yalniz-ag" to listOf(remote),
    )
    for ((name, chain) in chains) {
        println("zincir: $name")
        countOrigins(readProfileScreen(Repository(chain)))
    }
}
```

```text
zincir: tam
bellek: 2
disk: 1
ag: 1
varsayilan: 1
zincir: yalniz-ag
bellek: 0
disk: 0
ag: 3
varsayilan: 2
```

Çağıran işlev tek karakter değişmeden iki depoyla çalıştı. İkinci zincirde bellek ve disk sıfırdır. `ayar.tema` sunucuda olmadığı için varsayılana düştü ve `kampanya.kod` yine yedeğe kaldı. Çağıran kod, kaynak eklemek ya da çıkarmak için değişmedi; değişen tek şey köken dağılımı oldu.

## Sınırlayıcı Ölçüm

Depo sırayı değiştirirse ne olur? Önce ağa bakan bir zincirde aynı anahtarlar sunucudan gelir. Aynı üç anahtarı iki sırayla okuyalım: önce bellek, disk, ağ; sonra ağ, bellek, disk.

```kotlin
// Main.kt — sira degisince ayni anahtarin kokeni degisir
fun main() {
    val disk = Tier("disk", mapOf("profil.ad" to "Ayse", "urun.fiyat" to "120", "ayar.tema" to "koyu"))
    val remote = Tier("ag", mapOf("profil.ad" to "Ayse", "urun.fiyat" to "125", "urun.stok" to "8"))
    val memory = Tier("bellek", loadFrom(disk, listOf("profil.ad", "urun.fiyat")))
    val orders = listOf(
        "bellek-once" to listOf(memory, disk, remote),
        "ag-once" to listOf(remote, memory, disk),
    )
    for ((name, chain) in orders) {
        println("sira: $name")
        val repository = Repository(chain)
        listOf("profil.ad", "urun.fiyat", "ayar.tema")
            .map { repository.read(it.replace('.', '-'), it, "yok") }
            .forEach { it.show() }
    }
}
```

```text
sira: bellek-once
profil-ad: anahtar=profil.ad | deger=Ayse -> bellek
urun-fiyat: anahtar=urun.fiyat | deger=120 -> bellek
ayar-tema: anahtar=ayar.tema | deger=koyu -> disk
sira: ag-once
profil-ad: anahtar=profil.ad | deger=Ayse -> ag
urun-fiyat: anahtar=urun.fiyat | deger=125 -> ag
ayar-tema: anahtar=ayar.tema | deger=koyu -> disk
```

`profil.ad` iki sırada da `Ayse` değerini verdi, ama köken bellekten ağa geçti. Değer aynıydı; kural değeri değil, anahtarı ilk taşıyan basamağı görür. `urun.fiyat` ise hem kökenini hem değerini değiştirdi: 120 yerine 125. `ayar.tema` ağda bulunmadığı için her iki sırada da diskten geldi. Sıra, hangi basamağın sözü geçtiğini belirler; hangisinin doğru olduğunu belirlemez.

Ölçümün söylemediği şey, hangi sıranın iyi olduğudur. Önce belleğe bakan zincir ağa gitmeden yanıt verir, önce ağa bakan zincir güncel değeri alır. Kural bu iki yararı tartmaz; yalnız yanıtın hangi basamaktan geldiğini söyler.

**VK2.** Model, bir basamağın anahtarı ya taşıdığını ya taşımadığını varsayar. Platformda uzak kaynağın hata vermesi, zaman aşımı ya da yarım yanıt dönmesi modelde yoktur; modelde ağ basamağı her zaman cevap verir ya da hiç taşımaz.

## Özet

- Bu dersin kökeni: bellek. Depoya ilk giren okuma, zincirin başındaki basamaktan yanıtlanır.
- Depo kalıbı, yerel ve uzak veri kaynaklarını bir sıraya dizer ve çağırana tek bir okuma sunar.
- Aynı çağıran kod iki farklı depoyla değişmeden çalışır; değişen tek şey köken dağılımıdır: 2, 1, 1, 1 ile 0, 0, 3, 2.
- Sıra değişince aynı anahtarın kökeni değişir. `profil.ad` değerini korudu, `urun.fiyat` değerini de değiştirdi: 120 yerine 125.
- Kural sırayı görür, tazeliği görmez; bellekteki bayat değer sunucuya bakılmasını engeller.

## Sonraki Adım

Bu derste disk basamağı, hazır bir eşlemdi. Platformda disk, satırları ve sütunları olan bir veritabanıdır ve şeması zamanla değişir. Sıradaki ders, `Yerel Veritabanı`, şema değişince eski satırların hangi basamaktan okunduğunu ve yeni sütunun eski satırlarda neye düştüğünü ölçer.
