---
title: 'Atom Tabanlı Yaklaşımlar'
source: 'https://academia.sh/tr/kurslar/frontend-mimarisi/atom-tabanli-yaklasimlar'
course: 'Uygulama Mimarisi: Yönlendirme, Durum ve Veri'
language: tr
updated: '2026-08-17T18:09:21+00:00'
license: 'CC BY-SA 4.0'
---

# Atom Tabanlı Yaklaşımlar

İnce taneli tepkisel durum; atom ve türetilmiş atom, bağımlılık çizgesi, kirletme yayılımı ile tembel hesaplama, yeniden hesap ve uyarı sayısının merkezî depoyla karşılaştırılması, çizge modelinin bedeli.

Merkezî depo tek bir doğru kaynak kurdu, ama bir bedeli çıktı: her değişiklik tek bir durum
nesnesini yeniler ve tüm dinleyicilere duyurulur. Gereksiz çizimi seçiciler önler — yani
doğruluk, her tüketicinin kendi süzgecini kusursuz kurmasına bağlıdır. Bir seçici
unutulduğunda ya da her çağrıda yeni nesne ürettiğinde kusur sessizdir; ekran doğru
görünür, yalnız gereğinden çok çizilir.

Sorumluluğu ters çeviren bir model daha vardır. Durum tek bir büyük nesne olarak değil,
birbirine bağlı küçük birimler olarak tanımlanır. O zaman kimin neyi okuduğu bağımlılıktan
kendiliğinden çıkar ve süzme işi tüketiciden çizgeye geçer.

## Atom ve Türetilmiş Atom

**Atom**, bağımsız olarak okunup yazılabilen en küçük durum birimidir. Kuzey Yamaç Ölçüm
İstasyonu uygulamasında ölçüm listesi bir atom, süzgeç ölçütü bir atom, sıralama yönü bir
atom, arayüz teması bir atomdur. Aralarında kapsayan bir nesne yoktur; her biri kendi
başına durur.

**Türetilmiş atom** ise yazılamaz; girdilerinden hesaplanır. Süzülmüş liste, sıralanmış
liste ve özet birer türetilmiş atomdur. Durum Türleri dersindeki kural burada bir birim
olarak somutlaşır: türetilmiş değer saklanmaz, tanımlanır.

İkisi birlikte bir **bağımlılık çizgesi** kurar. Kenarlar okuma yönünde çizilir: süzülmüş
liste, ölçüm listesini ve süzgeci okur. Görünümler de bu çizgenin yaprakları gibi davranır;
okudukları düğümlere bağlanırlar.

Çizgenin işleyişi iki adımlıdır. Bir atom yazıldığında ondan erişilen tüm türetilmiş
düğümler **kirletilir** — değerleri artık geçerli sayılmaz. Kirlenmiş bir düğümün değeri
**okunduğunda** yeniden hesaplanır. Yazma anında hesap yapılmaz.

```js
// atom-cizgesi.mjs — atomlar, turetilmis atomlar, kirletme yayilimi ve yeniden hesap sayimi
const hesaplananlar = [];

function atom(ad, deger) {
  return { ad, tur: "atom", deger, bagimli: new Set() };
}

function turetilmis(ad, girdiler, hesap) {
  const d = { ad, tur: "turetilmis", girdiler, hesap, kirli: true, deger: undefined, bagimli: new Set() };
  for (const g of girdiler) g.bagimli.add(d);
  return d;
}

function tuketici(ad, girdiler) {
  const t = { ad, tur: "tuketici", girdiler, bagimli: new Set() };
  for (const g of girdiler) g.bagimli.add(t);
  return t;
}

function oku(d) {
  if (d.tur === "atom") return d.deger;
  if (d.kirli) {
    d.deger = d.hesap(...d.girdiler.map(oku));
    d.kirli = false;
    hesaplananlar.push(d.ad);
  }
  return d.deger;
}

function yaz(a, deger, etkin) {
  hesaplananlar.length = 0;
  a.deger = deger;
  const uyarilan = new Set();
  const kirlet = (d) => {
    for (const b of d.bagimli) {
      if (b.tur === "tuketici") { uyarilan.add(b.ad); continue; }
      if (b.kirli) continue;                      // zaten kirli, yayilim durur
      b.kirli = true;
      kirlet(b);
    }
  };
  kirlet(a);
  const cizilen = etkin.filter((t) => uyarilan.has(t.ad));   // ekranda olmayan uyarilmaz
  for (const t of cizilen) t.girdiler.forEach(oku);
  return { uyarilan: cizilen.map((t) => t.ad), hesaplanan: [...hesaplananlar] };
}

const olcumler = atom("olcumler", [
  { tur: "sicaklik", d: -4.2 }, { tur: "nem", d: 71 }, { tur: "sicaklik", d: -3.8 }]);
const suzgec = atom("suzgec", "hepsi");
const sira = atom("sira", "azalan");
const tema = atom("tema", "acik");

const suzulmus = turetilmis("suzulmus", [olcumler, suzgec],
  (o, s) => s === "hepsi" ? o : o.filter((x) => x.tur === s));
const siralanmis = turetilmis("siralanmis", [suzulmus, sira],
  (o, s) => [...o].sort((a, b) => s === "artan" ? a.d - b.d : b.d - a.d));
const ozet = turetilmis("ozet", [suzulmus],
  (o) => ({ sayi: o.length, ortalama: o.length ? o.reduce((t, x) => t + x.d, 0) / o.length : null }));
const basliklar = turetilmis("basliklar", [tema], (t) => ({ arkaPlan: t === "koyu" ? "#111" : "#fff" }));

const Liste = tuketici("Liste", [siralanmis]);
const OzetKarti = tuketici("OzetKarti", [ozet]);
const Baslik = tuketici("Baslik", [basliklar]);

let etkin = [Liste, OzetKarti, Baslik];
etkin.forEach((t) => t.girdiler.forEach(oku));          // ilk cizim

console.log("-- yazma basina uyarilan tuketici ve yeniden hesap --");
const adimlar = [
  ["tema = koyu", () => yaz(tema, "koyu", etkin)],
  ["sira = artan", () => yaz(sira, "artan", etkin)],
  ["suzgec = sicaklik", () => yaz(suzgec, "sicaklik", etkin)],
  ["olcumler += kayit", () => yaz(olcumler, [...olcumler.deger, { tur: "sicaklik", d: -5.1 }], etkin)],
];
let toplamUyari = 0, toplamHesap = 0;
for (const [etiket, calistir] of adimlar) {
  const { uyarilan, hesaplanan } = calistir();
  toplamUyari += uyarilan.length; toplamHesap += hesaplanan.length;
  console.log(etiket.padEnd(20), "uyarilan:", (uyarilan.join(", ") || "-").padEnd(26),
    "hesaplanan:", hesaplanan.join(", ") || "-");
}

console.log("-- OzetKarti ekrandan kaldirildi --");
etkin = [Liste, Baslik];
const s = yaz(suzgec, "nem", etkin);
toplamUyari += s.uyarilan.length; toplamHesap += s.hesaplanan.length;
console.log("suzgec = nem".padEnd(20), "uyarilan:", (s.uyarilan.join(", ") || "-").padEnd(26),
  "hesaplanan:", s.hesaplanan.join(", ") || "-");
console.log("ozet kirli mi:", ozet.kirli);

console.log("-- toplam --");
console.log("ince taneli  : uyari", toplamUyari, "| yeniden hesap", toplamHesap);
console.log("tek nesne    : uyari", 5 * 3, "| (her yazmada her tuketici)");
```

```
-- yazma basina uyarilan tuketici ve yeniden hesap --
tema = koyu          uyarilan: Baslik                     hesaplanan: basliklar
sira = artan         uyarilan: Liste                      hesaplanan: siralanmis
suzgec = sicaklik    uyarilan: Liste, OzetKarti           hesaplanan: suzulmus, siralanmis, ozet
olcumler += kayit    uyarilan: Liste, OzetKarti           hesaplanan: suzulmus, siralanmis, ozet
-- OzetKarti ekrandan kaldirildi --
suzgec = nem         uyarilan: Liste                      hesaplanan: suzulmus, siralanmis
ozet kirli mi: true
-- toplam --
ince taneli  : uyari 7 | yeniden hesap 10
tek nesne    : uyari 15 | (her yazmada her tuketici)
```

Her satır tek bir yazmanın etkisini gösterir. Tema değiştiğinde yalnız başlık düğümü
yeniden hesaplanır; ölçüm listesine dokunulmaz. Sıralama yönü değiştiğinde süzülmüş liste
yerinde kalır, yalnız sıralama yeniden yapılır — süzme işi tekrarlanmaz. Süzgeç
değiştiğinde ise üç düğüm birden kirlenir, çünkü ikisi süzülmüş listeye bağlıdır.

Yayılımın bir ayrıntısı koda yazılıdır: zaten kirli olan düğümde yayılım durur. Aynı yazma
sırasında bir düğüme iki yoldan ulaşılsa bile bir kez işaretlenir ve bir kez hesaplanır.

## Tembel Hesaplama

Özet kartı ekrandan kaldırıldıktan sonra yapılan yazma modelin ikinci kazancını gösterir.
Süzülmüş liste ve sıralanmış liste yeniden hesaplanır, çünkü liste hâlâ ekrandadır. Özet
düğümü ise yalnız **kirletilmiş**, hesaplanmamıştır; çıktının son satırı bunu doğrular.

Bunun nedeni hesabın yazmada değil okumada yapılmasıdır. Kimsenin okumadığı bir türetilmiş
değer hiç hesaplanmaz. Özet kartı yeniden ekrana geldiğinde okuma gerçekleşir ve değer o
anda üretilir.

Bu davranış Programlama Temelleri kursundaki tembel değerlendirmenin tepkisel karşılığıdır
ve pahalı türetmelerde tek başına belirleyici olabilir: bin satırlık bir tabloyu gruplayan
bir hesap, o tablo görünmediği sürece hiç çalışmaz.

## Kaba Taneli ile Karşılaştırma

Son iki satır iki modeli aynı senaryoda karşılaştırır. Beş yazmada ince taneli çizge yedi
tüketici uyarısı üretmiştir; tek nesneli depo, seçici kullanılmadığında on beş üretirdi.

Fark, seçicilerle kapatılabilir bir farktır — ama kapatılması gereken bir farktır. Atom
modelinde süzgeç, bağımlılığın kendisinden çıkar: bir tüketici neyi okuduysa ona bağlıdır.
Merkezî depoda ise süzgeç elle yazılır ve yanlış yazıldığında kimse uyarmaz.

Karşılığında kaybedilen şey de vardır. Merkezî depoda tüm değişimler tek bir eylem
akışından geçer; sıralanabilir, kaydedilebilir, yeniden oynatılabilir. Atom çizgesinde
yazma doğrudan atoma yapılır; "bu değeri kim değiştirdi" sorusunun tek bir yanıt noktası
yoktur. Eylem günlüğü ve yeniden oynatma bu modelde kendiliğinden gelmez.

## Çizgenin Bedeli

İnce tanelilik üç yeni sorun getirir.

**Dağınıklık.** Durum tanımı tek bir yerde toplanmaz; atomlar kullanıldıkları yerlerin
yanına dağılır. Küçük uygulamada bu bir kazançtır. Büyüdükçe, bir atomun kim tarafından
yazıldığını bulmak dosya taramaya dönüşür. Karşı önlem, yazma işlemlerini adlandırılmış
işlevlerin arkasına almak ve atomu doğrudan yazılabilir bırakmamaktır.

**Bağımlılık döngüsü.** Bir türetilmiş atomun dolaylı olarak kendini okuması, kirletme
yayılımını sonsuz döngüye sokar. Örnekteki kirletme yayılımı buna karşı kısmen korunur —
zaten kirli düğümde durur — ama okuma tarafı sonsuz özyinelemeye girer. Çizge, kurulurken
çevrimsizlik açısından denetlenmelidir; Veri Yapıları kursundaki yönlü çevrimsiz çizge
kavramı burada bir kısıttır.

**Kimlik.** Liste ögesi başına atom gerektiğinde — her ölçüm satırının kendi seçili durumu
gibi — atomlar bir aile oluşturur ve anahtarla üretilir. Bu ailenin temizlenmesi elle
yapılır; ekrandan kalkan ögenin atomu bırakılmazsa bellek sızıntısı oluşur. Eşzamansız
JavaScript ve Çalışma Zamanı kursundaki bellek sızıntısı tanısı bu durumda doğrudan
uygulanır.

## Hangi Model Nerede

İki model birbirinin alternatifi değil, farklı ölçütlere göre seçilen araçlardır.

Merkezî depo, değişimin **izlenebilirliği** birinci öncelikse tercih edilir: karmaşık iş
kuralları, geri alma gereksinimi, çok adımlı akışlar, hata bildirimiyle birlikte alınan
eylem günlüğü.

Atom modeli, **granülerlik** birinci öncelikse tercih edilir: birbirinden bağımsız çok
sayıda küçük durum, pahalı türetmeler, sık güncellenen ve az sayıda tüketicisi olan
değerler.

İkisi aynı uygulamada birlikte de bulunabilir. Ölçüt kalıbın kendisi değil, o durumun
hangi soruyu doğurduğudur.

## Özet

- Atom bağımsız okunup yazılabilen en küçük durum birimidir; türetilmiş atom yazılamaz,
  girdilerinden hesaplanır ve saklanmaz.
- Yazma, çizgede erişilen düğümleri kirletir; hesap yazmada değil okumada yapılır, böylece
  kimsenin okumadığı türetilmiş değer hiç hesaplanmaz.
- Kirletme yayılımı zaten kirli düğümde durduğu için bir yazmada her düğüm en çok bir kez
  hesaplanır.
- İnce taneli çizgede süzme işi bağımlılığın kendisinden çıkar; merkezî depoda aynı sonuç
  elle yazılan seçicilerle elde edilir ve yanlış yazıldığında sessizce kaybolur.
- Atom modelinde tek bir eylem akışı olmadığı için eylem günlüğü, yeniden oynatma ve geri
  alma kendiliğinden gelmez.
- Model seçimi ölçüte bağlıdır: izlenebilirlik merkezî depoyu, granülerlik atom çizgesini
  öne çıkarır; ikisi bir arada da kullanılabilir.

## Sonraki Adım

Bu iki modelin ortak bir varsayımı var: durumun sahibi uygulamadır, yazılan değer doğrudur.
Kuzey Yamaç Ölçüm İstasyonu uygulamasının en büyük durum kütlesi ise bu varsayımı
karşılamıyor. İstasyon listesi ve ölçüm kayıtları uzaktaki bir kaynağın kopyası; başka bir
kullanıcı yeni istasyon eklemiş, biri bir ölçümü düzeltmiş olabilir. Bu durumun sorusu
"nerede tutulacağı" değil, "ne kadar süre güncel sayılacağı". Sonraki ders sunucu durumunu
kendi katmanında ele alır: önbellek anahtarı, tazelik penceresi, aynı veriyi isteyen iki
bileşenin tek isteğe indirgenmesi, geçersizleştirme ve yeniden getirme.
