---
title: 'Toplamlar ve Kök'
source: 'https://academia.sh/tr/kurslar/alan-odakli-tasarim/toplamlar-ve-kok'
course: 'Alan Odaklı Tasarım'
language: tr
updated: '2026-08-17T18:10:41+00:00'
license: 'CC BY-SA 4.0'
---

# Toplamlar ve Kök

Tutarlılık sınırının çizilmesi: indirim tavanı değişmezinin kaç ayrı çağrı yolundan bozulabildiğinin beş yol üzerinde sayılması, kökten geçen erişimde bu sayının sıfıra inmesi, bir işlemde dokunulan toplama sayısının ölçülmesi ve toplamalar arası bağın kimlik atfına çevrilmesinin bedeli.

Buraya kadar gönderi tek başına ele alındı. Alanda tek başına değildir: indirimleri,
sözleşmesi ve beyan kalemleri vardır ve aralarında bir kural durur — indirim oranlarının
toplamı tavanı aşamaz. Bu kural gönderiye de indirimlere de tek başına ait değildir;
gönderiyle indirimlerinin bütününe aittir.

Böyle bir bütüne **toplama** (aggregate), o bütüne dışarıdan erişimin geçtiği tek nesneye
**toplama kökü** (aggregate root) denir. Toplama kavramı Tasarım Kalıpları kursundaki Depo ve
İş Birimi dersinde erişim ve yazma birimi olarak kurulmuştu. Bu dersin sorusu farklıdır:
sınır **tutarlılık** ölçütüne göre nereye çizilir — hangi değişmez sınırın içindedir ve sınır
korunuyor mu.

## Değişmez Kimin İçinde

Ölçüt tek cümlededir: bir değişmez, doğruluğunu denetlemek için gerekli bütün verinin
bulunduğu en küçük sınırın içine konur. İndirim tavanı için gereken veri bir gönderinin
indirim listesidir; başka gönderiye, başka sözleşmeye bakmak gerekmez. Sınır bu yüzden
gönderi ile indirimlerini kapsar, sözleşmeyi kapsamaz.

Sözleşmenin kendi değişmezi vardır: taahhüt edilen hacim aşılamaz. O kuralın girdisi
sözleşmeye bağlı bütün gönderilerin sayısıdır, dolayısıyla ikinci bir sınır doğar. İki sınır
arasındaki bağ nesne başvurusu değil **kimlik atfıdır**: gönderi sözleşme numarasını taşır,
sözleşme nesnesini taşımaz.

## Sınırı Dışa Açan Model

Birinci sürümde indirim listesi doğrudan erişilebilir ve tavan kuralı yalnız bir yardımcı
işlevin içindedir.

```sh
mkdir -p acik kok
```

```js
// acik/gonderi.mjs — indirimler disa acik; tavan kurali yalniz yardimci islevde
export const INDIRIM_TAVANI = 0.4;

export const gonderi = (gonderiNo, sozlesmeNo) =>
  ({ gonderiNo, sozlesmeNo, indirimler: [] });
export const toplamOran = (g) => g.indirimler.reduce((t, i) => t + i.oran, 0);

export function indirimEkle(g, indirim) {
  if (toplamOran(g) + indirim.oran > INDIRIM_TAVANI) throw new RangeError("tavan asildi");
  g.indirimler.push(indirim);
  return g;
}
```

```js
// acik/senaryo.mjs — sozlesmeli indirim: iki toplama ayni islemde yazilir
import { indirimEkle } from "./gonderi.mjs";

export function sozlesmeliIndirimUygula(g, sozlesme, oran, yaz) {
  indirimEkle(g, { ad: "sozlesme", oran });
  sozlesme.kullanilanHacim += 1;
  yaz("gonderi", g.gonderiNo);
  yaz("sozlesme", sozlesme.sozlesmeNo);
}
```

## Kökten Geçen Model

İkinci sürümde indirim listesi özel bir alandır. Dışa verilen liste bir kopyadır ve ögeleri
dondurulmuştur; ekleme yalnız kökün yönteminden geçer.

```js
// kok/gonderi.mjs — toplama koku: indirimlere yalniz kokten ulasilir
const INDIRIM_TAVANI = 0.4;

export class Gonderi {
  #indirimler = [];
  constructor(gonderiNo, sozlesmeNo) {
    this.gonderiNo = gonderiNo;
    this.sozlesmeNo = sozlesmeNo;
  }
  toplamOran() { return this.#indirimler.reduce((t, i) => t + i.oran, 0); }
  indirimler() { return this.#indirimler.map((i) => Object.freeze({ ...i })); }
  indirimEkle(indirim) {
    if (this.toplamOran() + indirim.oran > INDIRIM_TAVANI) throw new RangeError("tavan asildi");
    this.#indirimler.push(Object.freeze({ ...indirim }));
    return this;
  }
}
```

```js
// kok/senaryo.mjs — her islem tek toplama yazar; sozlesmeye kimlikle atif yapilir
export function sozlesmeliIndirimUygula(gonderi, oran, yaz) {
  gonderi.indirimEkle({ ad: "sozlesme", oran });
  yaz("gonderi", gonderi.gonderiNo);
}

export function sozlesmeKullanimiIsle(sozlesme, yaz) {
  sozlesme.kullanilanHacim += 1;
  yaz("sozlesme", sozlesme.sozlesmeNo);
}
```

## Ölçüm

İki ölçü alınır. Birincisi **ulaşılabilirlik**: 0,30 oranlı bir indirimi olan gönderide tavanı
aşan bir durum beş ayrı çağrı yolundan kaçında üretilebiliyor. İkincisi **işlem başına
toplama**: bir senaryo kaç ayrı toplamayı yazıyor.

```js
// sinir-olc.mjs — degismez kac yoldan bozulabiliyor, bir islemde kac toplama yaziliyor
import * as acik from "./acik/gonderi.mjs";
import { Gonderi } from "./kok/gonderi.mjs";
import { sozlesmeliIndirimUygula as acikSenaryo } from "./acik/senaryo.mjs";
import * as kokSenaryo from "./kok/senaryo.mjs";

const TAVAN = 0.4;
const EK = { ad: "hacim", oran: 0.5 };
const YENI = [{ ad: "hacim", oran: 0.9 }];

const ACIK_YOLLAR = {
  "alan yontemi": (g) => acik.indirimEkle(g, EK),
  "diziye ekleme": (g) => { g.indirimler.push({ ...EK }); },
  "ogenin orani": (g) => { g.indirimler[0].oran = 0.5; },
  "dizinin degistirilmesi": (g) => { g.indirimler = YENI.map((i) => ({ ...i })); },
  "toplu atama": (g) => { Object.assign(g, { indirimler: YENI.map((i) => ({ ...i })) }); },
};
const KOK_YOLLAR = {
  "alan yontemi": (g) => g.indirimEkle(EK),
  "diziye ekleme": (g) => { g.indirimler().push({ ...EK }); },
  "ogenin orani": (g) => { g.indirimler()[0].oran = 0.5; },
  "dizinin degistirilmesi": (g) => { g.indirimler = YENI.map((i) => ({ ...i })); },
  "toplu atama": (g) => { Object.assign(g, { indirimler: YENI.map((i) => ({ ...i })) }); },
};

function tara(ad, kur, oran, yollar) {
  let ihlal = 0;
  let hata = 0;
  for (const [yol, dene] of Object.entries(yollar)) {
    const g = kur();
    try { dene(g); } catch { hata += 1; }
    const bozuk = oran(g) > TAVAN;
    if (bozuk) ihlal += 1;
    console.log(`  ${yol.padEnd(24)} toplam oran ${oran(g).toFixed(2)}  ` +
      `${bozuk ? "IHLAL" : "korundu"}`);
  }
  console.log(`${ad}: 5 yoldan ${ihlal} tanesi degismezi bozdu, ${hata} tanesi hata verdi`);
}

console.log("acik model");
tara("acik", () => acik.indirimEkle(acik.gonderi("G-2001", "S-77"), { ad: "sozlesme", oran: 0.3 }),
  acik.toplamOran, ACIK_YOLLAR);
console.log("kok modeli");
tara("kok", () => new Gonderi("G-2001", "S-77").indirimEkle({ ad: "sozlesme", oran: 0.3 }),
  (g) => g.toplamOran(), KOK_YOLLAR);

function islemOlc(ad, kos) {
  const yazilan = [];
  kos((tur) => yazilan.push(tur));
  const tekil = new Set(yazilan);
  console.log(`${ad}: yazma cagrisi ${yazilan.length}, dokunulan toplama ${tekil.size} ` +
    `[${[...tekil].join(" ")}]`);
}

islemOlc("acik islem", (yaz) => {
  const g = acik.gonderi("G-2002", "S-77");
  acikSenaryo(g, { sozlesmeNo: "S-77", kullanilanHacim: 4 }, 0.25, yaz);
});
islemOlc("kok islemi", (yaz) => {
  const g = new Gonderi("G-2002", "S-77");
  kokSenaryo.sozlesmeliIndirimUygula(g, 0.25, yaz);
});
islemOlc("kok ikinci islemi", (yaz) => {
  kokSenaryo.sozlesmeKullanimiIsle({ sozlesmeNo: "S-77", kullanilanHacim: 4 }, yaz);
});
```

```sh
node sinir-olc.mjs
```

```
acik model
  alan yontemi             toplam oran 0.30  korundu
  diziye ekleme            toplam oran 0.80  IHLAL
  ogenin orani             toplam oran 0.50  IHLAL
  dizinin degistirilmesi   toplam oran 0.90  IHLAL
  toplu atama              toplam oran 0.90  IHLAL
acik: 5 yoldan 4 tanesi degismezi bozdu, 1 tanesi hata verdi
kok modeli
  alan yontemi             toplam oran 0.30  korundu
  diziye ekleme            toplam oran 0.30  korundu
  ogenin orani             toplam oran 0.30  korundu
  dizinin degistirilmesi   toplam oran 0.30  korundu
  toplu atama              toplam oran 0.30  korundu
kok: 5 yoldan 0 tanesi degismezi bozdu, 2 tanesi hata verdi
acik islem: yazma cagrisi 2, dokunulan toplama 2 [gonderi sozlesme]
kok islemi: yazma cagrisi 1, dokunulan toplama 1 [gonderi]
kok ikinci islemi: yazma cagrisi 1, dokunulan toplama 1 [sozlesme]
```

Açık modelde beş yolun dördü tavanı aşan bir gönderi üretiyor. Kuralın yazılı olduğu tek yol
— yardımcı işlev — hata veriyor; kalan dördü listeye doğrudan dokunuyor ve kuralı hiç
görmüyor. Kök modelinde aynı beş denemenin sıfırı değişmezi bozuyor: iki deneme hata
veriyor, ikisi kopya üzerinde çalıştığı için etkisiz kalıyor, biri kökün özel alanına
erişemiyor. Değişmezin korunması bir dikkat sorusu olmaktan çıkıp yapının verdiği bir güvence
oluyor.

Son üç satır sınırın genişliğini veriyor. Açık modelde tek bir işlem iki toplamayı yazıyor:
gönderi ile sözleşme birlikte kesinleşiyor. Kök modelinde aynı iş iki işleme bölünüyor ve her
biri tek toplamaya dokunuyor.

## Sınırın Bedeli

İşlem başına tek toplama kuralının bedeli açıktır: sözleşmenin kullanılan hacmi, gönderiye
indirim eklendiği anda güncellenmiş olmaz. İki toplama arasındaki tutarlılık **anlık
değildir**; ikinci işlem çalışana kadar sözleşme eski sayıyı taşır.

Bu, işlem sınırı seçiminden ayrı bir karardır. İşlem sınırının nereye çizileceği ve iki
yazmanın birlikte kesinleşmesi Veri Erişim Katmanı ve İş Mantığı kursundaki İşlem Sınırları
dersinde ölçülmüştü. Tutarlılık sınırı ondan önce gelir: hangi kuralın **anlık** korunması
gerektiğini söyler. İndirim tavanı anlık korunmalıdır, çünkü tavanı aşan bir gönderi bir an
için bile var olmamalıdır. Sözleşme hacminin sayımı anlık korunmak zorunda değildir, çünkü
alan bunu bir mutabakat sorusu olarak görür.

Ölçü bu yüzden iki yönlüdür. Sınır genişletilirse işlem başına toplama sayısı düşer ama
kilitlenen veri büyür; daraltılırsa yazma tek toplamaya iner ama anlık korunmayan kural sayısı
artar. Seçim, hangi kuralın bir an için bile bozulamayacağı sorusunun yanıtıdır.

## Kökün Kuralları

Ölçümdeki güvenceyi üreten üç kısıt vardır. Sınırın içindeki nesnelere dışarıdan başvuru
verilmez; verilecekse kopya verilir. Sınır dışındaki bir toplamaya kimlikle atıf yapılır,
nesneyle değil. Sınırın içini değiştiren her işlem kökün bir yönteminden geçer.

Üçüncü kısıt yukarıdaki ölçümde doğrudan görünüyor: `indirimler()` bir kopya döndürdüğü için
"diziye ekleme" yolu etkisiz kalıyor, ögeleri dondurulmuş olduğu için "ögenin oranı" yolu hata
veriyor. Kopya olmasa iki yol da değişmezi bozardı.

## Ne Zaman Uygulanmaz

Kökten geçen erişimin bedeli, sınırın içine bakmak isteyen her okumanın kökten geçmesi ve her
okumada kopya üretilmesidir. Değişmezi olmayan bir bütünde bu bedel karşılıksızdır: yalnız
birlikte okunan, hiçbir kuralı paylaşmayan nesneler için sınır çizmek bir dolaylılık
basamağından fazlasını üretmez.

İkinci durum sınırın büyüklüğüyle ilgilidir. Bir toplama, içindeki nesne sayısı büyüdükçe
pahalanır: her yazma bütünü yükler, her okuma kopya üretir. Sözleşmeye bağlı bütün gönderileri
tek toplamaya koymak indirim tavanını da sözleşme hacmini de anlık korur, karşılığında aynı
sözleşmedeki iki gönderinin aynı anda işlenmesini engeller. Sınırın doğru yeri, anlık
korunması gereken kural kümesinin en küçük kapsayıcısıdır.

## Özet

- Toplama, bir değişmezi paylaşan nesnelerin bütünüdür; toplama kökü o bütüne dışarıdan
  erişimin geçtiği tek nesnedir.
- Sınır ölçütü tutarlılıktır: bir değişmez, denetimi için gereken bütün verinin bulunduğu en
  küçük sınırın içine konur. İndirim tavanı gönderi ile indirimlerini kapsar, sözleşmeyi
  kapsamaz.
- Sınırı dışa açan modelde beş çağrı yolunun 4'ü tavanı aşan bir gönderi üretti; kökten geçen
  modelde 0'ı üretti.
- Açık modelde bir işlem 2 toplamaya dokundu; kök modelinde aynı iş iki işleme bölündü ve her
  biri 1 toplamaya dokundu.
- Bedel anlık olmayan tutarlılıktır: sözleşmenin hacim sayımı, gönderi yazıldığı anda
  güncellenmiş olmaz.
- Güvenceyi üç kısıt üretir — içeriye başvuru verilmez, sınır dışına kimlikle atıf yapılır,
  değişiklik kökten geçer.

## Sonraki Adım

Kökün yöntemleri kendi sınırının içindeki veriyle karar veriyor. Kitaplıkta bir de bu kalıba
uymayan bir davranış var: bir gönderiye uygulanacak en uygun tarifenin seçilmesi. Bu karar
gönderiyi, tarife kataloğunu ve müşterinin sözleşmesini birlikte gerektirir; üçünden hiçbiri
ötekilerin sahibi değildir. Kararı gönderiye yüklemek, gönderinin bilmek zorunda olduğu dış
ad sayısını artırır. Sonraki ders bu sayıyı ölçer, davranışın nesneye mi yoksa ayrı bir alan
servisine mi ait olduğunu ortak çalışan sayısıyla belirler ve alan servisini uygulama
servisinden ayıran ölçüyü kurar.
