---
title: 'Merkezî Depo Kalıbı'
source: 'https://academia.sh/tr/kurslar/frontend-mimarisi/merkezi-depo-kalibi'
course: 'Uygulama Mimarisi: Yönlendirme, Durum ve Veri'
language: tr
updated: '2026-08-17T18:09:21+00:00'
license: 'CC BY-SA 4.0'
---

# Merkezî Depo Kalıbı

Durumu tek yerde toplayan kalıp; tek yönlü veri akışı, saf indirgeyici, eylem sözleşmesi, seçicilerle gereksiz bildirimin süzülmesi, ara katmanların yeri ve kalıbın dolaylılık bedeli.

Durum türleri ayrıldı ama paylaşılan durumun nasıl tutulacağı açık değil. Ortak atada
tutulan bir değer, ağaç derinleştikçe onu kullanmayan onlarca bileşenin arasından
geçirilir. Daha ağır sorun, değişimin dağınıklığıdır: Kuzey Yamaç Ölçüm İstasyonu
uygulamasında bir satırın seçilmesi, bir ölçümün silinmesi ve oturumun kapatılması aynı
durumu üç ayrı yerden değiştirir. Ekranda yanlış bir değer göründüğünde onu kimin yazdığı
izlenemez.

**Merkezî depo** kalıbı bu iki soruna tek bir kısıtla yanıt verir: durum tek bir yerde
tutulur ve yalnız adlandırılmış eylemlerle değişir.

## Tek Yönlü Akış

Kalıbın omurgası dört durakta kapanan bir çevrimdir. Görünüm bir **eylem** gönderir; eylem
**indirgeyiciye** verilir; indirgeyici yeni durumu üretir; yeni durum görünüme akar.

Bu çevrimin dışında durumu değiştirmenin yolu yoktur. Kısıt, kalıbın bütün kazançlarının
kaynağıdır: değişimlerin tümü tek bir noktadan geçtiği için sıralanabilir, kaydedilebilir
ve yeniden oynatılabilir.

Üç parça ayrı ayrı tanımlanır. **Eylem**, olmuş bir olayı bildiren veri nesnesidir; bir
türü ve gerektiği kadar alanı vardır. **İndirgeyici**, o anki durumla eylemi alıp yeni
durumu döndüren saf bir işlevdir. **Depo**, o anki durumu tutan, eylem göndermeyi ve
değişimi izlemeyi sağlayan kaptır.

## İndirgeyici

İndirgeyici saf bir işlevdir: aynı girdi her zaman aynı çıktıyı verir, dışarıda hiçbir şey
değiştirmez. Ağ isteği yapmaz, rastgele sayı üretmez, o anki zamanı okumaz.

Durumu **değiştirmez**, yenisini üretir. Bu, Programlama Temelleri kursundaki
değiştirilemezlik kuralının doğrudan uygulanmasıdır ve pratik bir sonucu vardır: bir
dilimin referansı değişmediyse o dilim değişmemiştir. Bu ucuz sınama, görünümün ne zaman
yeniden çizileceğini belirler.

```js
// merkezi-depo.mjs — indirgeyici, eylem dizisi, ara katman, secici ve yeniden oynatma
const BASLANGIC = { istasyonlar: [], secili: null, suzgec: "hepsi" };

function indirgeyici(durum, eylem) {
  switch (eylem.tur) {
    case "istasyonlar/yuklendi":
      return { ...durum, istasyonlar: eylem.veri };
    case "istasyon/secildi":
      return durum.istasyonlar.some((i) => i.kimlik === eylem.kimlik)
        ? { ...durum, secili: eylem.kimlik }
        : durum;                                   // gecersiz secim durumu degistirmez
    case "istasyon/eklendi":
      return { ...durum, istasyonlar: [...durum.istasyonlar, eylem.istasyon] };
    case "istasyon/silindi":
      return {
        ...durum,
        istasyonlar: durum.istasyonlar.filter((i) => i.kimlik !== eylem.kimlik),
        secili: durum.secili === eylem.kimlik ? null : durum.secili,
      };
    case "suzgec/degisti":
      return { ...durum, suzgec: eylem.deger };
    default:
      return durum;                                // bilinmeyen eylem: ayni referans
  }
}

function depoKur(indirgeyici, baslangic, araKatmanlar = []) {
  let durum = baslangic;
  const dinleyiciler = new Set();
  const temel = (eylem) => {
    durum = indirgeyici(durum, eylem);
    for (const d of dinleyiciler) d(durum);
  };
  const gonder = araKatmanlar.reduceRight(
    (sonraki, katman) => katman({ oku: () => durum })(sonraki), temel);
  return { oku: () => durum, gonder, izle: (f) => dinleyiciler.add(f) };
}

const gunluk = [];
const kutukKatmani = ({ oku }) => (sonraki) => (eylem) => {
  const onceki = oku();
  sonraki(eylem);
  gunluk.push({ eylem: eylem.tur, degisti: oku() !== onceki, durum: oku() });
};

const EYLEMLER = [
  { tur: "istasyonlar/yuklendi", veri: [{ kimlik: "kuzey-yamac" }, { kimlik: "dogu-sirt" }] },
  { tur: "istasyon/secildi", kimlik: "kuzey-yamac" },
  { tur: "suzgec/degisti", deger: "sicaklik" },
  { tur: "istasyon/secildi", kimlik: "bati-vadi" },     // listede yok
  { tur: "istasyon/eklendi", istasyon: { kimlik: "bati-vadi" } },
  { tur: "istasyon/secildi", kimlik: "bati-vadi" },
  { tur: "istasyon/silindi", kimlik: "bati-vadi" },
  { tur: "tema/degisti", deger: "koyu" },               // indirgeyici tanimiyor
];

const depo = depoKur(indirgeyici, BASLANGIC, [kutukKatmani]);

// Secici: bilesenin ilgilendigi dilim. Referans degismediyse yeniden cizim gerekmez.
const seciciSayaci = { bildirim: 0, listeDegisti: 0 };
let oncekiListe = depo.oku().istasyonlar;
depo.izle((d) => {
  seciciSayaci.bildirim += 1;
  if (d.istasyonlar !== oncekiListe) { seciciSayaci.listeDegisti += 1; oncekiListe = d.istasyonlar; }
});

for (const e of EYLEMLER) depo.gonder(e);

console.log("-- eylem gunlugu --");
console.log("#".padEnd(3), "eylem".padEnd(24), "degisti".padEnd(8), "istasyon", "secili");
gunluk.forEach((g, i) => console.log(
  String(i + 1).padEnd(3), g.eylem.padEnd(24), String(g.degisti).padEnd(8),
  String(g.durum.istasyonlar.length).padEnd(8), String(g.durum.secili)));

console.log("-- dinleyici --");
console.log("gonderilen eylem:", EYLEMLER.length,
  "| bildirim:", seciciSayaci.bildirim,
  "| liste dilimi degisti:", seciciSayaci.listeDegisti);

// Indirgeyici saf oldugu icin ayni dizi ayni sonucu verir.
console.log("-- yeniden oynatma --");
const yeniden = EYLEMLER.reduce(indirgeyici, BASLANGIC);
console.log("son durum ayni mi:", JSON.stringify(yeniden) === JSON.stringify(depo.oku()));
console.log("3. eylemden sonrasi:", JSON.stringify(EYLEMLER.slice(0, 3).reduce(indirgeyici, BASLANGIC)));
console.log("son durum          :", JSON.stringify(depo.oku()));
```

```
-- eylem gunlugu --
#   eylem                    degisti  istasyon secili
1   istasyonlar/yuklendi     true     2        null
2   istasyon/secildi         true     2        kuzey-yamac
3   suzgec/degisti           true     2        kuzey-yamac
4   istasyon/secildi         false    2        kuzey-yamac
5   istasyon/eklendi         true     3        kuzey-yamac
6   istasyon/secildi         true     3        bati-vadi
7   istasyon/silindi         true     2        null
8   tema/degisti             false    2        null
-- dinleyici --
gonderilen eylem: 8 | bildirim: 8 | liste dilimi degisti: 3
-- yeniden oynatma --
son durum ayni mi: true
3. eylemden sonrasi: {"istasyonlar":[{"kimlik":"kuzey-yamac"},{"kimlik":"dogu-sirt"}],"secili":"kuzey-yamac","suzgec":"sicaklik"}
son durum          : {"istasyonlar":[{"kimlik":"kuzey-yamac"},{"kimlik":"dogu-sirt"}],"secili":null,"suzgec":"sicaklik"}
```

Günlüğün dördüncü ve sekizinci satırları aynı davranışı iki nedenle gösterir. Listede
bulunmayan bir istasyonun seçilmesi reddedilir; indirgeyici o anki durumu **aynı referansla**
geri döndürür. Tanınmayan eylem türü de aynı biçimde ele alınır. Her iki durumda da hiçbir
görünümün yeniden çizilmesi gerekmez ve bunu anlamak için tek bir referans karşılaştırması
yeter.

Yedinci satır tek eylemin birden çok dilimi etkileyebildiğini gösterir. Seçili istasyon
silindiğinde hem liste kısalır hem de seçim boşaltılır; bu iki değişiklik ayrı eylemlere
bölünmez, çünkü aralarında ekranın tutarsız kalacağı bir an oluşmamalıdır. Değişmez şudur:
seçili kimlik ya boştur ya da listede bulunur.

## Yeniden Oynatma

Son bölüm saflığın karşılığını gösterir. Eylem dizisi baştan indirgendiğinde depoda oluşan
durumun aynısı elde edilir. Depoya ihtiyaç bile yoktur; durum, başlangıç değeri ile eylem
dizisinin bir fonksiyonudur.

Bunun üç kullanımı vardır. Kullanıcıdan gelen bir hata bildirimi eylem günlüğüyle birlikte
alındığında sorun yerel olarak yeniden üretilebilir. Herhangi bir ana dönmek, o ana kadarki
eylemleri yeniden indirgemekle olur — çıktıda üçüncü eylemden sonraki durum böyle
üretilmiştir. Ve geri alma işlevi, silinen eylemi çıkarıp diziyi yeniden indirgeyerek
kurulur.

Bu olanaklar indirgeyicinin saflığına bağlıdır. İçine o anki zamanı okuyan tek bir satır
konduğunda yeniden oynatma farklı sonuç verir ve üçü birden kaybolur.

## Seçici

Dinleyici bölümü kalıbın en sık atlanan maliyetini sayıyla verir: sekiz eylem gönderilmiş,
sekiz bildirim üretilmiş, ama istasyon listesi yalnız üç kez değişmiştir.

Depo, durumun hangi bölümünün değiştiğini bilmez; her gönderimde tüm dinleyicilere haber
verir. Bileşenlerin bu bildirimleri süzmesi gerekir. **Seçici**, durumdan bileşenin
ilgilendiği dilimi çıkaran işlevdir; dilimin referansı değişmediyse yeniden çizim yapılmaz.

Seçicinin bir tuzağı vardır. Her çağrıda yeni bir nesne ya da dizi üreten seçici — süzme,
eşleme veya nesne toplama yapan bir seçici — referans karşılaştırmasını her zaman
başarısız kılar ve süzgeç hiç çalışmaz. Bu tür seçiciler girdileri değişmediği sürece aynı
sonucu döndürecek biçimde bellenir.

## Ara Katman

İndirgeyici saf olmak zorundaysa, ağ isteği, kütüğe yazma ve depolamaya kaydetme nereye
konur? **Ara katman**, eylem gönderme yolunun üzerine yerleşen ve eylemi indirgeyiciye
ulaşmadan önce ya da ulaştıktan sonra işleyen sarmalayıcıdır.

Örnekteki kütük katmanı eylemi geçirir, sonra öncesi ve sonrasındaki durumu karşılaştırıp
kaydeder. Aynı yerde başka sorumluluklar da durur: bir eylemi eşzamansız bir işleme
çevirmek, belirli eylemlerden sonra durumu depolamaya yazmak, gelen eylemi doğrulamak.

Ara katmanların sırası anlamlıdır: dıştaki katman içtekini sarar. Kütük katmanı en dışta
durursa eşzamansız katmanın ürettiği eylemleri de görür; en içte durursa yalnız
indirgeyiciye ulaşanları.

## Kalıbın Bedeli

Merkezî depo bedelsiz değildir. Bir alanı değiştirmek için eylem türü tanımlanır,
indirgeyiciye dal eklenir, gönderme yazılır; üç dosyaya dokunulur. Bu dolaylılık tek bir
şey karşılığında alınır: değişimin izlenebilirliği. İzlenebilirliğin değeri olmadığı yerde
bedel de gereksizdir.

Buradan iki sınır çıkar. Yerel durum depoya konmaz; bir açılır listenin açıklığı için eylem
tanımlamak, kazanç getirmeyen bir dolaylılıktır. Sunucu durumu da konmaz — kopyalanabilir
ama tazelik, yeniden getirme ve istek tekrarı soruları indirgeyicinin çözemeyeceği
sorulardır; o katman bu konunun dördüncü dersinin konusudur.

Depoya giren şey, uygulamanın kendi ürettiği ve birden çok yerden değiştirilen paylaşılan
durumdur.

## Özet

- Merkezî depo kalıbı durumu tek yerde toplar ve değişimi yalnız adlandırılmış eylemlerle
  kabul eder; akış görünüm, eylem, indirgeyici ve durum arasında tek yönde kapanır.
- İndirgeyici saf bir işlevdir ve durumu değiştirmeden yenisini üretir; değişmeyen dilimin
  referansı korunur, bu da yeniden çizim kararını tek bir karşılaştırmaya indirir.
- Saflık, eylem dizisinden durumu yeniden üretmeyi sağlar: hata yeniden oynatılabilir,
  geçmiş bir ana dönülebilir, geri alma kurulabilir.
- Depo her gönderimde tüm dinleyicilere haber verir; gereksiz çizimi seçiciler süzer ve
  yeni nesne üreten seçiciler bellenmezse süzgeç çalışmaz.
- Yan etkiler ara katmanlarda durur; katman sırası hangi eylemlerin görüleceğini belirler.
- Kalıbın bedeli dolaylılıktır; yerel durum ve sunucu durumu depoya konmaz.

## Sonraki Adım

Merkezî depo tek bir doğru kaynak kurdu, ama bunun bir bedeli çıktı: her değişiklik tek bir
durum nesnesini yeniler ve tüm dinleyicilere duyurulur. Gereksiz çizim, doğru yazılmış
seçicilerle önlenir — yani doğruluk, her tüketicinin kendi süzgecini kusursuz kurmasına
bağlıdır. Bu sorumluluğun ters çevrildiği bir yaklaşım daha vardır: durumu tek bir büyük
nesne olarak değil, birbirine bağlı küçük birimler olarak tanımlamak. O zaman kimin neyi
okuduğu bağımlılıktan kendiliğinden çıkar ve yalnız etkilenen tüketiciler haberdar edilir.
Sonraki ders bu ince taneli modeli, atomları ve türetilmiş atomları ele alır.
