---
title: 'Kalite Nitelikleri ve Yönetişim'
source: 'https://academia.sh/tr/kurslar/mimari-yonetisim'
language: tr
updated: '2026-08-17T04:42:30+00:00'
license: 'CC BY-SA 4.0'
lessons: 10
outcomes:
    - 'Kalite niteliklerini mimari kararlara bağlar'
    - 'Uygunluk işlevleriyle mimariyi sürekli sınar'
    - 'Güvenlik ve uyumluluk gereksinimlerini mimariye gömer'
---

# Kalite Nitelikleri ve Yönetişim

Kararların hangi niteliği koruduğunun yazılması ve korunmanın sürdüğünün denetlenmesi: başarım, erişilebilirlik, güvenlik, sürdürülebilirlik ve taşınabilirliği kapsayan nitelik ağacı, bir niteliğin ötekini bastırdığı gerilimler, kimlik, sır yönetimi ve güven sınırlarıyla güvenlik mimarisi, ölçek biriminin seçilmesi, değişim maliyetini gözleyen sürdürülebilirlik ölçütleri, merkezî, federatif ve danışman yönetişim modelleri, otomatik mimari kural denetimi yapan uygunluk işlevleri, yapısal kuralların derlemede sınanması, değişime dayanıklı evrimsel mimari ve düzenleyici kısıtların mimariye etkisi.

## Kalite Nitelikleri

Kararın hangi niteliği koruduğunun yazılması: başarım, erişilebilirlik, güvenlik, sürdürülebilirlik ve taşınabilirliği bir ağaçta toplamak, bir niteliği yükseltmenin ötekini bastırdığı gerilimleri ölçmek, kimlik, sır yönetimi ve güven sınırlarıyla güvenlik mimarisini kurmak, ölçek biriminin seçilmesi ve değişim maliyetini gözleyen sürdürülebilirlik ölçütleri.

### Nitelik Ağacı

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/nitelik-agaci

Kalite niteliklerinin alt niteliklere ve yapraklara ayrılması: otuz yaprağın kaçının bir sayıya bağlandığı, kaçının koşturulabilir denetime çevrildiği, bilinen ihlal kümesinde kaç ihlalin yakalanıp kaçtığı ve çevrilemeyen yaprakların yerine konan düzenin kişi-saat bedeli.

Önceki kurs bağları kurdu, sahipliği ayırdı, dışarıdan alınacak ile içeride geliştirilecek
arasındaki sınırı çizdi. Kararlar gerekçeyle yazıldı; ama gerekçeler yan yana konduğunda ortak bir
boşluk görünür: hiçbiri hangi **niteliği** koruduğunu yazmadı. "Katalog dışarıdan alınır" kararı
neyi savunuyor — geliştirme süresini mi, sürdürülebilirliği mi, taşınabilirliği mi? Yanıt yazılı
değilse karar sonradan tartışılamaz, çünkü tartışmanın ölçeği yoktur.

Kalite niteliğinin tanımı ve nitelik senaryosunun alanları Mimari Karar ve Dokümantasyon
kursunda kuruldu, sınanabilirliği orada ölçüldü; burada tekrarlanmaz. Buradaki
soru bir adım geridedir: elde hangi nitelikler var, hangi alt niteliklere ayrılıyor, uçtaki her
yaprak bir sayıya bağlanabiliyor mu. Kursun ayırt edici ölçüsü de buradan çıkar — bir
nitelik **koşturulabilir bir denetime** çevrilebiliyor mu, çevrilemiyorsa yerine ne konuyor.

#### Ağacın Kurulması

Aşağıdaki blok, kurgusal bölgesel kütüphane ağının nitelik ağacını **modeller**: beş kök nitelik,
her biri alt niteliklere, onlar da yapraklara ayrılır. Yaprak, ağacın ölçü taşıyan tek yeridir;
kök ile alt nitelik yalnız katalog başlığıdır.

```js
// agac.mjs — nitelik agacini modeller ve her yapragin olculebilirligini sayar
import { mkdirSync, writeFileSync } from "node:fs";

// KN1: bolgesel kutuphane agi kurgudur (subeler, disaridan alinmis katalog, icerde yazilan
// odunc ve ucretlendirme servisleri, ayri uyelik sistemi, belediyedeki kimlik servisi). Agac
// bir modeldir: [kod, kok/alt, yaprak, gozlem, birim, esik, yon, kaynak]; kaynak "oto" =
// kayittan okunur, "elle" = olcum oturumu ister, "-" = sayiya baglanmaz.
const TANIM = [
  ["B1", "basarim/yanit", "katalog arama gecikmesi", "arama_ms", "ms", 400, "kucuk", "oto"],
  ["B2", "basarim/yanit", "odunc islemi gecikmesi", "odunc_ms", "ms", 600, "kucuk", "oto"],
  ["B3", "basarim/yanit", "rapor ekrani acilisi", "rapor_sn", "sn", 8, "kucuk", "oto"],
  ["B8", "basarim/yanit", "yavasligin uyeye hissettirdigi", "", "", null, "", "-"],
  ["B4", "basarim/hacim", "saatlik odunc islemi", "odunc_saat", "islem", 1200, "buyuk", "oto"],
  ["B5", "basarim/hacim", "toplu katalog yuklemesi", "yukleme_dk", "dk", 90, "kucuk", "oto"],
  ["B6", "basarim/kaynak", "uc birim bellegi", "bellek_mb", "MB", 512, "kucuk", "oto"],
  ["B7", "basarim/kaynak", "baglanti havuzu dolulugu", "havuz", "%", 80, "kucuk", "oto"],
  ["E1", "erisilebilirlik/kesinti", "aylik merkez kesintisi", "kesinti_dk", "dk", 45, "kucuk", "oto"],
  ["E2", "erisilebilirlik/kesinti", "kopukken odunc surmesi", "kopuk_dk", "dk", 120, "buyuk", "elle"],
  ["E3", "erisilebilirlik/kurtarma", "yedege gecis suresi", "gecis_sn", "sn", 60, "kucuk", "elle"],
  ["E4", "erisilebilirlik/kurtarma", "veri kaybi penceresi", "kayip_dk", "dk", 5, "kucuk", "oto"],
  ["E5", "erisilebilirlik/yayilim", "cokmede etkilenen sube", "etkilenen", "sube", 1, "kucuk", "elle"],
  ["E6", "erisilebilirlik/yayilim", "kesintide uyeye verilen bilgi", "", "", null, "", "-"],
  ["G1", "guvenlik/kimlik", "dogrulamasiz uc nokta", "acik_uc", "uc", 0, "kucuk", "oto"],
  ["G2", "guvenlik/kimlik", "rolsuz personel islemi", "rolsuz", "islem", 0, "kucuk", "oto"],
  ["G3", "guvenlik/gizlilik", "depoda acik sir degeri", "acik_sir", "deger", 0, "kucuk", "oto"],
  ["G4", "guvenlik/gizlilik", "gecmisin sinir disina cikmasi", "sinir_disi", "alan", 0, "kucuk", "oto"],
  ["G5", "guvenlik/izlenebilirlik", "kaydi tutulmayan yonetici islemi", "kayitsiz", "islem", 0, "kucuk", "oto"],
  ["G6", "guvenlik/izlenebilirlik", "guven sinirinin dogru yerde olmasi", "", "", null, "", "-"],
  ["S1", "surdurulebilirlik/degisim", "ucretlendirmede dokunulan dosya", "dosya", "dosya", 6, "kucuk", "oto"],
  ["S2", "surdurulebilirlik/degisim", "yeni sube ekleme elle adimi", "adim", "adim", 3, "kucuk", "elle"],
  ["S3", "surdurulebilirlik/anlasilirlik", "modul bagimlilik derinligi", "derinlik", "duzey", 5, "kucuk", "oto"],
  ["S4", "surdurulebilirlik/anlasilirlik", "kodun okunur olmasi", "", "", null, "", "-"],
  ["S5", "surdurulebilirlik/sinanabilirlik", "odunc kurali kapsamasi", "kapsama", "%", 70, "buyuk", "oto"],
  ["S6", "surdurulebilirlik/sinanabilirlik", "sinama takimi kosumu", "sinama_dk", "dk", 12, "kucuk", "oto"],
  ["T1", "tasinabilirlik/kurulum", "uc birim kurulum suresi", "kurulum_dk", "dk", 45, "kucuk", "elle"],
  ["T2", "tasinabilirlik/kurulum", "ortama ozgu cagri", "ozgu_cagri", "cagri", 0, "kucuk", "oto"],
  ["T3", "tasinabilirlik/veri", "aktarimda kaybolan alan", "kaybolan", "alan", 0, "kucuk", "oto"],
  ["T4", "tasinabilirlik/veri", "uyelik verisinin devri", "", "", null, "", "-"],
];

const ALAN = ["kod", "dal", "ad", "gozlem", "birim", "esik", "yon", "kaynak"];
const YAPRAK = TANIM.map((t) => Object.fromEntries(ALAN.map((a, i) => [a, t[i]])));

// Olculebilirlik iki basamakta olculur: yaprak bir sayiya baglaniyor mu (birimi ve esigi
// var mi), o sayi kosturulabilir denetime cevriliyor mu (gozlemi bir kayittan okunuyor mu).
const sayili = (y) => y.esik !== null;
const kosar = (y) => sayili(y) && y.kaynak === "oto";

mkdirSync("agac", { recursive: true });
writeFileSync("agac/agac.mjs", `export const YAPRAK = ${JSON.stringify(YAPRAK)};\n`);

const kok = (y) => y.dal.split("/")[0];
const say = (l, f) => String(l.filter(f).length).padStart(9);
console.log(`${"kok nitelik".padEnd(20)}${"alt".padStart(4)}${"yaprak".padStart(8)}` +
  `${"denetim".padStart(9)}${"elle".padStart(9)}${"sayisiz".padStart(9)}`);
for (const k of [...new Set(YAPRAK.map(kok))]) {
  const y = YAPRAK.filter((n) => kok(n) === k);
  console.log(`${k.padEnd(20)}${String(new Set(y.map((n) => n.dal)).size).padStart(4)}` +
    `${String(y.length).padStart(8)}${say(y, kosar)}` +
    `${say(y, (n) => n.kaynak === "elle")}${say(y, (n) => !sayili(n))}`);
}
const s = YAPRAK.filter(sayili), o = YAPRAK.filter(kosar);
console.log(`toplam ${YAPRAK.length} yaprak: ${s.length} sayiya baglanir, ` +
  `${YAPRAK.length - s.length} baglanmaz; ${o.length} tanesi kosturulabilir denetime ` +
  `cevrilir, ${s.length - o.length} tanesi elle olcum ister`);
const dizi = (f) => YAPRAK.filter(f).map((y) => `${y.kod} ${y.ad}`).join("; ");
console.log(`\nsayiya baglanamayan: ${dizi((y) => !sayili(y))}`);
console.log(`esigi olup kaydi olmayan: ${dizi((y) => sayili(y) && y.kaynak === "elle")}`);
```

```
kok nitelik          alt  yaprak  denetim     elle  sayisiz
basarim                3       8        7        0        1
erisilebilirlik        3       6        2        3        1
guvenlik               3       6        5        0        1
surdurulebilirlik      3       6        4        1        1
tasinabilirlik         2       4        2        1        1
toplam 30 yaprak: 25 sayiya baglanir, 5 baglanmaz; 20 tanesi kosturulabilir denetime cevrilir, 5 tanesi elle olcum ister

sayiya baglanamayan: B8 yavasligin uyeye hissettirdigi; E6 kesintide uyeye verilen bilgi; G6 guven sinirinin dogru yerde olmasi; S4 kodun okunur olmasi; T4 uyelik verisinin devri
esigi olup kaydi olmayan: E2 kopukken odunc surmesi; E3 yedege gecis suresi; E5 cokmede etkilenen sube; S2 yeni sube ekleme elle adimi; T1 uc birim kurulum suresi
```

Otuz yaprağın 25'i bir sayıya bağlanıyor, beşi bağlanmıyor. Asıl ayrım ikinci basamakta: bu 25'in
yalnız 20'si koşturulabilir bir denetime çevriliyor. Aradaki beş yaprağın
eşiği de birimi de yazılı — eksik olan **gözlem**. Yedeğe geçişin 60 saniyenin altında kalıp
kalmadığı bir tatbikat kurulmadan öğrenilemez. Eşiği yazmak ile ölçümü bir kayda bağlamak ayrı
işlerdir ve ikincisi ödenmediğinde nitelik ölçülü görünüp denetimsiz kalır.

Kök başına dağılım eşit değil. Güvenliğin altı yaprağının beşi denetime çevriliyor, çünkü çoğu bir
sayımdır: kaç uç nokta kimlik doğrulamasız, depoda kaç açık sır değeri var. Erişilebilirliğin altı
yaprağının yalnız ikisi çevriliyor; kalanı sistemi bozmayı gerektiriyor. Bu, hangi niteliğin daha
önemli olduğunu söylemez; hangisinin **daha ucuz denetlendiğini** söyler.

#### Bilinen İhlal Kümesinde Denetim

Yirmi denetim yazıldı; sıradaki soru bunların gerçekte ne yakaladığıdır. Aşağıdaki blok yirmi dört
gözlem penceresini ve bu pencerelerde kayıtlı on sekiz ihlali **modelliyor**, denetimleri kümeye
uyguluyor, sonra çevrilemeyenin yerine konan düzeni aynı kaçan ihlaller üzerinde ölçüyor.

```js
// denetim.mjs — yapraklardan denetim uretip bilinen ihlal kumesine uygular, sonra denetime
// cevrilemeyen yapraklarin yerine konani olcer
import { YAPRAK } from "./agac/agac.mjs";

// KN2: iki ceyreklik 24 gozlem penceresinde 18 ihlal kayitli (kurgu). Ucuncu alan ihlalin
// gozlem akisina yansiyip yansimadigi: G4 ihlali kayda gecmeyen bir yoldan olmus. Dorduncu
// alan ihlalin kac pencere surdugu; yapisal kusurlar duzeltilene dek surer (99).
const IHLAL = [
  [2, "B1", true, 1], [3, "G3", true, 4], [4, "E3", false, 20], [5, "S1", true, 1],
  [6, "B5", true, 2], [7, "G6", false, 99], [8, "E1", true, 1], [9, "B7", true, 3],
  [10, "T1", false, 1], [11, "G4", false, 99], [12, "S4", false, 99], [13, "B1", true, 1],
  [14, "S5", true, 6], [15, "E4", true, 1], [16, "S2", false, 10], [17, "G1", true, 5],
  [18, "B8", false, 99], [20, "S3", true, 8],
];
const PENCERE = 24;

// KN3: gurultu modeli. Her otomatik yapragin pencere degeri esigin cevresinden duzgun
// dagilimla cekilir; esik dagilimin ust ucuna yakindir, boylece ihlal olmadan da esigin
// asildigi pencereler dogar. Uretec kendi yazilmistir, tohum gorunurdur.
const uretec = (tohum) => () => {
  tohum = (tohum + 0x6d2b79f5) | 0;
  let t = Math.imul(tohum ^ (tohum >>> 15), 1 | tohum);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
const rastgele = uretec(41307);

const OTO = YAPRAK.filter((y) => y.kaynak === "oto");
const bul = (kod) => YAPRAK.find((y) => y.kod === kod);
const cek = (y, r) => (y.esik === 0 ? (r < 0.06 ? 1 : 0)
  : y.esik * (y.yon === "buyuk" ? 0.95 + r * 0.65 : 0.4 + r * 0.65));
const ihlalDegeri = (y) => (y.esik === 0 ? 2 : y.esik * (y.yon === "buyuk" ? 0.8 : 1.3));

const kayit = [];
for (let p = 1; p <= PENCERE; p += 1) {
  const gozlem = {};
  for (const y of OTO) gozlem[y.gozlem] = cek(y, rastgele());
  const ih = IHLAL.find(([n]) => n === p);
  if (ih && ih[2]) gozlem[bul(ih[1]).gozlem] = ihlalDegeri(bul(ih[1]));
  kayit.push({ p, gozlem, ihlal: ih ? ih[1] : null });
}

// Denetim yapragin kendisinden uretilir: gozlem alani, esik ve yon disinda bilgi almaz.
const denetim = (y) => (g) => (y.yon === "buyuk" ? g[y.gozlem] < y.esik : g[y.gozlem] > y.esik);
const atesleme = [];
for (const k of kayit)
  for (const y of OTO) if (denetim(y)(k.gozlem)) atesleme.push([k.p, y.kod, k.ihlal === y.kod]);

const yakalanan = atesleme.filter(([, , d]) => d);
const yanlis = atesleme.filter(([, , d]) => !d);
const kacan = IHLAL.filter(([p, kod]) => !yakalanan.some(([q, k]) => q === p && k === kod));
console.log(`${PENCERE} pencere, ${IHLAL.length} bilinen ihlal, ${OTO.length} kosturulabilir denetim`);
console.log(`yakalanan ${yakalanan.length}, kacan ${kacan.length}, yanlis alarm ${yanlis.length}\n`);

const alarm = {};
for (const [, kod] of yanlis) alarm[kod] = (alarm[kod] || 0) + 1;
const sirali = Object.entries(alarm).sort((a, b) => b[1] - a[1]);
console.log(`${sirali.length} denetim en az bir kez bosa otmus; en cok oten ucu: ` +
  sirali.slice(0, 3).map(([k, n]) => `${k} (${n})`).join(", "));

// KN4: ekip davranisi. Iki ceyrekte ikiden cok yanlis alarm ureten denetim kapatilir.
const kapali = sirali.filter(([, n]) => n > 2).map(([k]) => k);
const kalan = yakalanan.filter(([, kod]) => !kapali.includes(kod));
console.log(`kapatma esigi 2: kapanan denetim ${kapali.length} (${kapali.join(", ")}); ` +
  `sonrasinda yakalanan ${kalan.length}, kacan ${IHLAL.length - kalan.length}, ` +
  `yanlis alarm ${yanlis.filter(([, kod]) => !kapali.includes(kod)).length}`);
const tablo = OTO.reduce((s, y) => s + `${y.gozlem}${y.birim}${y.esik}${y.yon}`.length, 0);
console.log(`denetim maliyeti: kural govdesi 1 satir (hepsi icin ayni), esik tablosu ` +
  `${OTO.length} satir / ${tablo} karakter, kosum ${PENCERE * OTO.length} karsilastirma, ` +
  `doldurulan gozlem alani ${PENCERE * OTO.length}`);

// KN5: cevrilemeyenin yerine iki duzen konur; oturumlar 12. ve 24. pencerede kosar. Esigi
// olup kaydi olmayan bes yaprak icin olcum oturumu (6 kisi-saat), sayiya baglanamayan bes
// yaprak icin alanin %35'ini orneklem alan gozden gecirme (4 kisi-saat). Otomatik yapraga
// dusen kacan ihlal hicbir duzenin kapsaminda degildir.
const OTURUM = [12, 24], ORNEKLEM = 0.35, SAAT = { olcum: 6, gozden: 4 };
const duzen = (y) => (y.kaynak === "elle" ? "olcum" : y.esik === null ? "gozden" : "kapsam disi");
const ikinci = uretec(9152);

console.log(`\n${"pencere".padStart(7)}  ${"kod".padEnd(4)}${"yerine konan".padEnd(14)}` +
  `${"sonuc".padEnd(11)}gecikme`);
let bulunan = 0, gecikme = 0;
for (const [p, kod, , surme] of kacan) {
  const d = duzen(bul(kod));
  let o = OTURUM.find((n) => n >= p && n <= p + surme &&
    (d === "olcum" || (d === "gozden" && ikinci() < ORNEKLEM)));
  if (d === "kapsam disi") o = undefined;
  if (o) { bulunan += 1; gecikme += o - p; }
  console.log(`${String(p).padStart(7)}  ${kod.padEnd(4)}${d.padEnd(14)}` +
    `${(o ? "yakalandi" : "kacti").padEnd(11)}${o ? o - p : "-"}`);
}
const kapsam = YAPRAK.filter((y) => y.kaynak !== "oto");
const saat = kapsam.reduce((s, y) => s + SAAT[duzen(y)], 0) * OTURUM.length;
console.log(`\nkacan ${kacan.length} ihlalin ${kacan.length - 1} tanesi yerine konanin kapsaminda: ` +
  `${bulunan} yakalandi, ${kacan.length - 1 - bulunan} kacti; biri hicbir duzende degil`);
console.log(`ortalama gecikme ${(gecikme / bulunan).toFixed(1)} pencere ` +
  `(kosturulabilir denetimde 0), maliyet ${saat} kisi-saat, ` +
  `yakalanan basina ${(saat / bulunan).toFixed(0)} kisi-saat`);
```

```
24 pencere, 18 bilinen ihlal, 20 kosturulabilir denetim
yakalanan 11, kacan 7, yanlis alarm 23

15 denetim en az bir kez bosa otmus; en cok oten ucu: B1 (3), B4 (3), G3 (2)
kapatma esigi 2: kapanan denetim 2 (B1, B4); sonrasinda yakalanan 9, kacan 9, yanlis alarm 17
denetim maliyeti: kural govdesi 1 satir (hepsi icin ayni), esik tablosu 20 satir / 359 karakter, kosum 480 karsilastirma, doldurulan gozlem alani 480

pencere  kod yerine konan  sonuc      gecikme
      4  E3  olcum         yakalandi  8
      7  G6  gozden        kacti      -
     10  T1  olcum         kacti      -
     11  G4  kapsam disi   kacti      -
     12  S4  gozden        yakalandi  12
     16  S2  olcum         yakalandi  8
     18  B8  gozden        yakalandi  6

kacan 7 ihlalin 6 tanesi yerine konanin kapsaminda: 4 yakalandi, 2 kacti; biri hicbir duzende degil
ortalama gecikme 8.5 pencere (kosturulabilir denetimde 0), maliyet 100 kisi-saat, yakalanan basina 25 kisi-saat
```

On sekiz ihlalin 11'i yakalanıyor, yedisi kaçıyor. Kaçanların üçü eşiği olup kaydı olmayan, üçü
sayıya bağlanamayan yapraklarda: bu altısı beklenen kayıp, ağaç haber vermişti.
Yedincisi başkadır. On birinci penceredeki ihlal, denetimi yazılı ve eşiği sıfır olan bir yaprakta
gerçekleşti, yine de kaçtı — ihlal o yaprağın gözlem alanından geçmedi. **Bir denetimin kapsamı,
beslendiği gözlem alanının kapsamı kadardır**; eşik doğru yazıldığı için denetimin doğru çalıştığı
sanılır.

Yanlış alarm 23, yakalanan ihlalin iki katından fazla; on beş denetim boşa öttü. Bu, denetimin
hatası değil eşiğin dağılıma göre konumunun sonucu: eşik gözlenen değerlerin üst ucuna yakınsa
ihlal olmayan pencerelerde de aşılır. Modelde ekip iki
çeyrekte ikiden çok yanlış alarm üreten denetimi kapatıyor; iki denetim kapanıyor ve yakalanan
ihlal 11'den 9'a düşüyor. **Yanlış alarmın bedeli harcanan inceleme zamanı değil, kapanan
denetimin bundan sonra yakalamayacağı ihlallerdir.** Bu üçlü — yakalanan, kaçan, yanlış alarm —
sınama kurslarındaki yanlış geçme ve yanlış kalma ikilisiyle aynı olgu; ölçülen sınama durumu
değil mimari kuraldır.

Denetimin kendi maliyeti dört kalemde duruyor: kural gövdesi bir satır ve yirmi yaprağın hepsinde
aynı, çünkü kural yaprağın eşiğinden üretiliyor; eşik tablosu yirmi satır ve 359 karakter; koşum 480
karşılaştırma; dördüncü ve büyük kalem, 480 gözlem alanının doldurulması. Kuralın pahalı yeri metni
değil beslendiği veridir.

#### Çevrilemeyenin Yerine Konan

Denetime çevrilemeyen on yaprak boşta bırakılmıyor: eşiği olup kaydı olmayan beşi için çeyrekte bir
ölçüm oturumu, sayıya bağlanamayan beşi için gözden geçirme konuyor. Kaçan yedi ihlalin
altısı bu iki düzenin kapsamında; dördü yakalanıyor, ikisi kaçıyor. Biri tek pencere sürüp kapandı:
oturum on ikinci pencerede koştuğunda ortada bir şey kalmamıştı. Örneklemle çalışan gözden geçirme
de bir kusuru atladı — inceleme alanın üçte birine bakıyor, kusur oraya düşmedi.

Ortalama gecikme 8,5 pencere; koşturulabilir denetimde gecikme sıfırdır, ihlal olduğu pencerede
görünür. Maliyet iki çeyrekte 100 kişi-saat, yakalanan ihlal başına 25 kişi-saat; yirmi denetim aynı
sürede insan emeği harcamadan 11 ihlal yakaladı. Elle düzenin karşılığı da var: yanlış alarm
üretmiyor, kararı veren insan bağlamı görüyor. Geriye kimsenin kapsamadığı bir ihlal
kalıyor; ağaç bunu da adıyla yazmayı sağlıyor — kapsanmayan yaprak değil, **kapsanmayan yol** var.

#### Özet

- Nitelik ağacı kök, alt nitelik ve yapraktan oluşur; ölçü yalnız yaprakta durur ve otuz yaprağın
  25'i bir sayıya bağlanır, beşi bağlanmaz.
- Sayıya bağlanmak denetlenebilmek değildir: 25 sayılı yaprağın 20'si denetime çevrilir, beşi eşiği
  yazılı olduğu halde gözlemi olmadığından elle ölçüm ister.
- Yirmi denetim, on sekiz ihlalin 11'ini yakalıyor, yedisini kaçırıyor, 23 yanlış alarm üretiyor;
  biri denetimi yazılı olduğu halde gözlem alanına yansımadığı için kaçıyor.
- Yanlış alarmın bedeli ölçülür: ikiden çok alarm üreten iki denetim kapatıldığında yakalanan ihlal
  11'den 9'a iniyor.
- Çevrilemeyenin yerine konan düzen kaçan altı ihlalin dördünü 8,5 pencere gecikme ve 100 kişi-saate
  yakalıyor; koşturulabilir denetimin bedeli bir satır kural ve 480 gözlem alanıdır.

#### Sonraki Adım

Ağaç kurulduğunda her yaprak kendi eşiğiyle tek başına denetleniyormuş gibi durur. Sonraki ders bu
görüntüyü bozar: yapraklar birbirinden bağımsız değil. Bağlantı havuzunun doluluğunu düşüren karar
yedeğe geçiş süresini uzatır, sınama kapsamasını yükselten karar toplu yükleme penceresini taşırır.
Bu ilişki tahminle değil ölçüyle bulunur — bir gerilim matrisi kurulur, bir niteliği yükselten
kararlar sırayla uygulanır, her uygulamadan sonra öteki niteliklerde ölçülen düşüş sayılır. Sonuçta
kaç çiftin çeliştiği, en çok bastırılan niteliğin hangisi olduğu ve hiçbir kararın iyileştiremediği
bir niteliğin bulunup bulunmadığı çıkar.

### Nitelikler Arası Gerilim

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/nitelikler-arasi-gerilim

Bir niteliği yükselten kararın ötekilerde ölçülen düşüşü: on iki karardan gerilim matrisinin kurulması, çelişen çiftlerin ve en çok bastırılan niteliğin sayılması, hiçbir kararın iyileştirmediği niteliğin bulunması ve gerileme denetiminin bilinen ihlal kümesinde yakaladığı, kaçırdığı, boşa öttüğü durumların ölçülmesi.

Önceki ders ağacı kurdu ve her yaprağı kendi eşiğine bağladı. O tabloda nitelikler birbirinden
bağımsız durur: her yaprağın ayrı eşiği, ayrı gözlem alanı, ayrı denetimi vardır. Ama bir mimari
karar tek bir yaprağa dokunmaz. Arama gecikmesini düşüren önbellek katmanı, aynı anda değişimde
dokunulan dosya sayısını ve ortama özgü çağrı sayısını yükseltir. Yaprak başına denetim bunu
göremez, çünkü her denetim kendi yaprağına bakar.

Bir niteliğin diğerini bastırması yeni bir gözlem değil; erişilebilirlik ile tutarlılık arasındaki
gerilim Sistem Tasarımına Giriş kursunda, temel nitelikler konusunda ölçüldü ve burada
tekrarlanmaz. Buradaki soru daha genel: **bir karar listesi varken hangi nitelik çiftlerinin
çeliştiği ve hangi niteliğin sistemli olarak bastırıldığı sayılabilir mi.** Kursun sorusu da
sürer: bu gerilim koşturulabilir bir denetime çevrilebiliyor mu.

#### Gerilim Matrisi

Aşağıdaki blok, kurgusal bölgesel kütüphane ağının on dört ölçülen yaprağını, bir çeyrekte
önerilen on iki kararı ve kararların bu yapraklara etkisini **modelliyor**. Kök niteliğin skoru,
yapraklarının eşiğe göre payının ortalamasıdır; kararın etkisi, uygulanmadan önceki ve sonraki
skor farkıdır.

```js
// gerilim.mjs — nitelikleri ve kararlari modelleyip gerilim matrisini kurar
import { mkdirSync, writeFileSync } from "node:fs";

// KN6: kurgusal bolgesel kutuphane aginin on dort olculen yapragi ve baslangic degerleri.
// [kod, kok nitelik, ad, birim, esik, yon, baslangic]. Model.
const YAPRAK = [
  ["P1", "basarim", "arama gecikmesi", "ms", 400, "kucuk", 310],
  ["P2", "basarim", "odunc islemi gecikmesi", "ms", 600, "kucuk", 420],
  ["P3", "basarim", "saatlik odunc islemi", "islem", 1200, "buyuk", 1500],
  ["E1", "erisilebilirlik", "aylik kesinti", "dk", 45, "kucuk", 22],
  ["E2", "erisilebilirlik", "yedege gecis", "sn", 60, "kucuk", 38],
  ["E3", "erisilebilirlik", "veri kaybi penceresi", "dk", 5, "kucuk", 2],
  ["G1", "guvenlik", "kimlik dogrulama kapsami", "%", 100, "buyuk", 96],
  ["G2", "guvenlik", "sir degeri tazeleme araligi", "gun", 90, "kucuk", 60],
  ["G3", "guvenlik", "denetim kaydi kapsami", "%", 90, "buyuk", 94],
  ["S1", "surdurulebilirlik", "degisimde dokunulan dosya", "dosya", 6, "kucuk", 4],
  ["S2", "surdurulebilirlik", "bagimlilik derinligi", "duzey", 5, "kucuk", 3],
  ["S3", "surdurulebilirlik", "sinama kapsamasi", "%", 70, "buyuk", 78],
  ["T1", "tasinabilirlik", "ortama ozgu cagri", "cagri", 12, "kucuk", 7],
  ["T2", "tasinabilirlik", "uc birim kurulumu", "dk", 45, "kucuk", 30],
];

// KN7: bir ceyrekte onerilen on iki mimari karar. Her karar, baslangic degerlerine gore
// dokundugu yapraklarin yeni degerini verir; dokunmadigi yaprak degismez. Model.
const KARAR = [
  ["arama onbellegi", { P1: 180, S1: 5, S2: 4, T1: 9 }],
  ["es zamanli cogaltma", { E3: 0.5, E2: 30, P2: 560 }],
  ["her istekte kimlik dogrulama", { G1: 100, P1: 360, P2: 470 }],
  ["sir degerinin tazelenmesi", { G2: 30, S1: 5, T1: 10 }],
  ["denetim kaydinin yayilmasi", { G3: 99, P3: 1380, S1: 5 }],
  ["katman ayrimi", { S2: 2, S1: 3, P2: 450 }],
  ["sinama takimini genisletme", { S3: 88 }],
  ["subede yerel kuyruk", { E1: 12, T2: 40, S1: 5, S2: 4 }],
  ["otomatik yedege gecis", { E2: 20, E1: 18, S2: 4, G3: 90 }],
  ["istek basina hiz sinirlama", { E1: 16, P3: 1320, P1: 330 }],
  ["uyelik verisini ayirma", { G1: 99, S1: 6, P2: 500, T2: 38 }],
  ["toplu yuklemeyi geceye alma", { P1: 290, P3: 1560, E3: 3 }],
];

const KOK = [...new Set(YAPRAK.map((y) => y[1]))];
// Pay: kucuk yonlu yaprakta esik-deger, buyuk yonluda deger-esik; esige bolunerek
// birimsizlestirilir. Kok niteligin skoru, yapraklarinin pay ortalamasidir.
const pay = ([, , , , esik, yon], deger) =>
  (yon === "kucuk" ? esik - deger : deger - esik) / esik;
const skor = (deger) => Object.fromEntries(KOK.map((k) => {
  const y = YAPRAK.filter((n) => n[1] === k);
  return [k, y.reduce((s, n) => s + pay(n, deger[n[0]]), 0) / y.length];
}));

const BASLANGIC = Object.fromEntries(YAPRAK.map((y) => [y[0], y[6]]));
const temel = skor(BASLANGIC);
const DUYARLIK = 0.005;   // bu buyuklugun altindaki skor degisimi degisim sayilmaz

const etki = KARAR.map(([ad, degisim]) => {
  const d = skor({ ...BASLANGIC, ...degisim });
  const delta = Object.fromEntries(KOK.map((k) => [k, d[k] - temel[k]]));
  return { ad, degisim, delta };
});

mkdirSync("model", { recursive: true });
writeFileSync("model/model.mjs",
  `export const YAPRAK = ${JSON.stringify(YAPRAK)};\n` +
  `export const KARAR = ${JSON.stringify(KARAR)};\n` +
  `export const BASLANGIC = ${JSON.stringify(BASLANGIC)};\n`);

const kisa = (k) => k.slice(0, 4);
console.log(`${"karar".padEnd(30)}${"yukselen".padEnd(22)}dusen (olculen dusus)`);
for (const e of etki) {
  const yuk = KOK.filter((k) => e.delta[k] > DUYARLIK);
  const dus = KOK.filter((k) => e.delta[k] < -DUYARLIK);
  console.log(`${e.ad.padEnd(30)}${(yuk.map(kisa).join(",") || "-").padEnd(22)}` +
    `${dus.map((k) => `${kisa(k)} ${(-e.delta[k] * 100).toFixed(1)}`).join(", ") || "-"}`);
}

// Gerilim matrisi: satir yukselen, sutun dusen nitelik; hucre o ikiliyi ureten karar sayisi.
const matris = Object.fromEntries(KOK.map((a) => [a, Object.fromEntries(KOK.map((b) => [b, 0]))]));
const dususToplam = Object.fromEntries(KOK.map((k) => [k, 0]));
const yukselisSayisi = Object.fromEntries(KOK.map((k) => [k, 0]));
for (const e of etki) {
  const yuk = KOK.filter((k) => e.delta[k] > DUYARLIK);
  const dus = KOK.filter((k) => e.delta[k] < -DUYARLIK);
  for (const a of yuk) { yukselisSayisi[a] += 1; for (const b of dus) matris[a][b] += 1; }
  for (const b of dus) dususToplam[b] += -e.delta[b];
}

console.log(`\ngerilim matrisi (satir yukselir, sutun duser; hucre karar sayisi)`);
console.log(`${"".padEnd(20)}${KOK.map((k) => kisa(k).padStart(7)).join("")}`);
for (const a of KOK)
  console.log(`${a.padEnd(20)}${KOK.map((b) => (a === b ? "-" : String(matris[a][b])).padStart(7)).join("")}`);

const cift = KOK.flatMap((a) => KOK.filter((b) => a !== b && matris[a][b] > 0).map((b) => [a, b]));
const karsilikli = cift.filter(([a, b]) => matris[b][a] > 0).length / 2;
console.log(`\ncelisen sirali cift ${cift.length}/${KOK.length * (KOK.length - 1)}, ` +
  `bunlarin ${karsilikli} tanesi karsilikli (iki yonde de bastirma var)`);
const dusuren = (k) => etki.filter((e) => e.delta[k] < -DUYARLIK).length;
const enCok = KOK.slice().sort((a, b) => dususToplam[b] - dususToplam[a])[0];
console.log(`en cok bastirilan nitelik: ${enCok}; ${dusuren(enCok)} kararda ` +
  `${KOK.filter((a) => matris[a][enCok] > 0).length} ayri nitelik ugruna dusuruluyor, ` +
  `toplam olculen dusus ${(dususToplam[enCok] * 100).toFixed(1)} pay puani`);
for (const k of KOK)
  if (yukselisSayisi[k] === 0)
    console.log(`hicbir kararin iyilestirmedigi nitelik: ${k}; ${KARAR.length} kararin ` +
      `${dusuren(k)} tanesi dusuruyor, hicbiri yukseltmiyor`);
```

```
karar                         yukselen              dusen (olculen dusus)
arama onbellegi               basa                  surd 12.2, tasi 8.3
es zamanli cogaltma           eris                  basa 7.8
her istekte kimlik dogrulama  guve                  basa 6.9
sir degerinin tazelenmesi     guve                  surd 5.6, tasi 12.5
denetim kaydinin yayilmasi    guve                  basa 3.3, surd 5.6
katman ayrimi                 surd                  basa 1.7
sinama takimini genisletme    surd                  -
subede yerel kuyruk           eris                  surd 12.2, tasi 11.1
otomatik yedege gecis         eris                  guve 1.5, surd 6.7
istek basina hiz sinirlama    eris                  basa 6.7
uyelik verisini ayirma        guve                  basa 4.4, surd 11.1, tasi 8.9
toplu yuklemeyi geceye alma   basa                  eris 6.7

gerilim matrisi (satir yukselir, sutun duser; hucre karar sayisi)
                       basa   eris   guve   surd   tasi
basarim                   -      1      0      1      1
erisilebilirlik           2      -      1      2      1
guvenlik                  3      0      -      3      2
surdurulebilirlik         1      0      0      -      0
tasinabilirlik            0      0      0      0      -

celisen sirali cift 11/20, bunlarin 2 tanesi karsilikli (iki yonde de bastirma var)
en cok bastirilan nitelik: surdurulebilirlik; 6 kararda 3 ayri nitelik ugruna dusuruluyor, toplam olculen dusus 53.3 pay puani
hicbir kararin iyilestirmedigi nitelik: tasinabilirlik; 12 kararin 4 tanesi dusuruyor, hicbiri yukseltmiyor
```

Yirmi sıralı nitelik çiftinin 11'inde çelişki ölçülüyor: bir niteliği yükselten en az bir karar
ötekini düşürüyor. İkisi karşılıklı — başarım ile erişilebilirlik, başarım ile sürdürülebilirlik.
Karşılıklı çift, hangi yönde ilerlenirse ilerlensin ötekinin düştüğü çifttir; kapatılamaz, yalnız
düşüşün payı seçilebilir.

En çok bastırılan nitelik sürdürülebilirlik: on iki kararın altısı onu düşürüyor, üç ayrı nitelik
uğruna, toplam 53,3 pay puanı. Sürdürülebilirlik yapraklarının ikisi — değişimde dokunulan dosya ve
bağımlılık derinliği — hemen her ekleme kararında artar. **Bir parça eklemek başarım ya da güvenlik
için seçilir; bedelini her seferinde sürdürülebilirlik öder.**

Matrisin son satırı sıfırlarla dolu. Hiçbir karar taşınabilirliği yükseltmiyor; dördü düşürüyor.
Bu ölçü taşınabilirliğin önemsiz olduğunu göstermez — kimsenin onu hedefleyen bir karar
önermediğini gösterir. Eşiği yazmak, o niteliği savunan bir kararın önerileceği anlamına gelmiyor;
matris, listede olmayan kararı görünür kılan tek satırdır.

Bir karar hiçbir niteliği düşürmüyor: sınama takımını genişletme. Ölçülen on dört yaprakta bu
kararın bedelini taşıyan yaprak yok; ölçülmeyen bedel matriste sıfır görünür. **Matris, ölçülen
yapraklar kadar dürüsttür.**

#### Gerilemenin Denetimi

Matris bir tablo doldurur, kendiliğinden bir şey engellemez. Sıradaki soru, gerilimin
koşturulabilir bir denetime çevrilip çevrilemeyeceğidir: bir karar uygulandığında hiçbir kök
niteliğin skoru belirli bir eşikten fazla düşmemeli. Aşağıdaki blok bu denetimi bilinen ihlal
kümesine uyguluyor ve eşik seçiminin ne değiştirdiğini sayıyor.

```js
// gerileme.mjs — gerilimi kosturulabilir bir gerileme denetimine cevirir ve bilinen ihlal
// kumesinde yakalanan / kacan / yanlis alarm sayar
import { YAPRAK, KARAR, BASLANGIC } from "./model/model.mjs";

// KN8: bilinen ihlal kumesi. On iki karar uygulandiktan sonra bes tanesi kayitlarda
// "kabul edilemez gerileme" olarak isaretlenmis; kalan yedisinin dususu bilerek kabul
// edilmis takastir. Bu isaret bir yargidir, olcumden cikmaz. Model.
const IHLAL = ["arama onbellegi", "sir degerinin tazelenmesi", "subede yerel kuyruk",
  "uyelik verisini ayirma", "katman ayrimi"];

const KOK = [...new Set(YAPRAK.map((y) => y[1]))];
const TAVAN = { "%": 100 };   // yuzde tasiyan yaprak 100'u asamaz
const pay = ([, , , , esik, yon], d) => (yon === "kucuk" ? esik - d : d - esik) / esik;
const skor = (deger) => Object.fromEntries(KOK.map((k) => {
  const y = YAPRAK.filter((n) => n[1] === k);
  return [k, y.reduce((s, n) => s + pay(n, deger[n[0]]), 0) / y.length];
}));
const temel = skor(BASLANGIC);
const enBuyukDusus = ([, degisim]) => {
  const d = skor({ ...BASLANGIC, ...degisim });
  return Math.max(0, ...KOK.map((k) => (temel[k] - d[k]) * 100));
};

// Denetim: bir karar uygulandiginda hicbir kok niteligin skoru esikten cok dusmemeli.
// Esigin nereye konacagi olcumden cikmaz; asagidaki tarama secenekleri sayar.
console.log(`${"esik (pay puani)".padEnd(18)}${"atesleme".padStart(9)}${"yakalanan".padStart(10)}` +
  `${"kacan".padStart(7)}${"yanlis alarm".padStart(14)}`);
for (const esik of [2, 4, 6, 8, 10, 12]) {
  const oten = KARAR.filter((k) => enBuyukDusus(k) > esik).map(([ad]) => ad);
  const yak = oten.filter((ad) => IHLAL.includes(ad));
  console.log(`${String(esik).padEnd(18)}${String(oten.length).padStart(9)}` +
    `${String(yak.length).padStart(10)}${String(IHLAL.length - yak.length).padStart(7)}` +
    `${String(oten.length - yak.length).padStart(14)}`);
}

const SECILEN = 6;
const oten = KARAR.filter((k) => enBuyukDusus(k) > SECILEN).map(([ad]) => ad);
console.log(`\nsecilen esik ${SECILEN} pay puani`);
for (const ad of IHLAL.filter((a) => !oten.includes(a)))
  console.log(`  kacan: ${ad} (en buyuk dusus ` +
    `${enBuyukDusus(KARAR.find(([k]) => k === ad)).toFixed(1)} pay puani, esigin altinda)`);
for (const ad of oten.filter((a) => !IHLAL.includes(a)))
  console.log(`  yanlis alarm: ${ad} (dususu kayitta kabul edilmis)`);

// Birikimli etki: kararlar sirayla uygulanir, her etki o yapragin degerini kendi baslangicina
// gore carpan olarak degistirir. Tekil denetim her karara ayri baktigi icin birikimi goremez.
const son = { ...BASLANGIC };
for (const [, degisim] of KARAR)
  for (const [kod, yeni] of Object.entries(degisim)) {
    const y = YAPRAK.find((n) => n[0] === kod);
    son[kod] = Math.min(son[kod] * (yeni / BASLANGIC[kod]), TAVAN[y[3]] ?? Infinity);
  }

const asan = YAPRAK.filter((y) => pay(y, son[y[0]]) < 0);
const tekil = KARAR.flatMap(([ad, d]) =>
  YAPRAK.filter((y) => y[0] in d && pay(y, d[y[0]]) < 0).map((y) => [ad, y[0]]));
console.log(`\ntekil kararda esigi asan (karar, yaprak) ikilisi ${tekil.length} ` +
  `(${tekil.map(([a, k]) => `${a}/${k}`).join(", ")}); on iki karar birlikte uygulandiginda ` +
  `esigi asan yaprak ${asan.length}, ${[...new Set(asan.map((n) => n[1]))].length} nitelikte`);
for (const y of asan)
  console.log(`  ${y[0]}  ${y[2].padEnd(26)} ${BASLANGIC[y[0]]} -> ${son[y[0]].toFixed(1)} ` +
    `${y[3]} (esik ${y[4]})`);

// KN9: cevrilemeyen kisim. Denetim dususu olcer, kabul edilebilirligini olcemez; bu bir
// yargidir. Yerine muafiyet kaydi konur ve denetim kayda bakar.
const muafiyet = oten.filter((a) => !IHLAL.includes(a));
console.log(`\ncevrilemeyen: dususun kabul edilebilirligi (yargi). Yerine ${muafiyet.length} ` +
  `muafiyet kaydi konuyor; kayittan sonra yanlis alarm ${muafiyet.length} -> 0, ` +
  `yakalanan ${oten.length - muafiyet.length}, kacan ${IHLAL.length - (oten.length - muafiyet.length)}`);
console.log(`denetim maliyeti: karar basina ${YAPRAK.length} yeniden olcum, ` +
  `${KARAR.length} kararda ${KARAR.length * YAPRAK.length} olcum; ` +
  `kosum ${KARAR.length * KOK.length + YAPRAK.length} karsilastirma; ` +
  `bakim ${muafiyet.length} muafiyet kaydi (her karar degistiginde gozden gecirilir)`);
```

```
esik (pay puani)   atesleme yakalanan  kacan  yanlis alarm
2                        10         4      1             6
4                        10         4      1             6
6                         9         4      1             5
8                         4         4      1             0
10                        4         4      1             0
12                        3         3      2             0

secilen esik 6 pay puani
  kacan: katman ayrimi (en buyuk dusus 1.7 pay puani, esigin altinda)
  yanlis alarm: es zamanli cogaltma (dususu kayitta kabul edilmis)
  yanlis alarm: her istekte kimlik dogrulama (dususu kayitta kabul edilmis)
  yanlis alarm: otomatik yedege gecis (dususu kayitta kabul edilmis)
  yanlis alarm: istek basina hiz sinirlama (dususu kayitta kabul edilmis)
  yanlis alarm: toplu yuklemeyi geceye alma (dususu kayitta kabul edilmis)

tekil kararda esigi asan (karar, yaprak) ikilisi 1 (uyelik verisini ayirma/G1); on iki karar birlikte uygulandiginda esigi asan yaprak 4, 3 nitelikte
  P2  odunc islemi gecikmesi     420 -> 799.3 ms (esik 600)
  S1  degisimde dokunulan dosya  4 -> 11.0 dosya (esik 6)
  T1  ortama ozgu cagri          7 -> 12.9 cagri (esik 12)
  T2  uc birim kurulumu          30 -> 50.7 dk (esik 45)

cevrilemeyen: dususun kabul edilebilirligi (yargi). Yerine 5 muafiyet kaydi konuyor; kayittan sonra yanlis alarm 5 -> 0, yakalanan 4, kacan 1
denetim maliyeti: karar basina 14 yeniden olcum, 12 kararda 168 olcum; kosum 74 karsilastirma; bakim 5 muafiyet kaydi (her karar degistiginde gozden gecirilir)
```

Eşik taraması tek bir eşiğin işi bitirmediğini gösteriyor. Sekiz pay puanında denetim beş bilinen
ihlalin dördünü yakalıyor ve hiç boşa ötmüyor; altıya indirilince yakalama artmıyor, yanlış alarm
beşe çıkıyor. İkiye kadar gevşetilse bile beşinci ihlal kaçıyor. Nedeni duyarlılık değil: o kararın
en büyük düşüşü 1,7 pay puanı ve kabul edilemez sayılmasının nedeni düşüşün büyüklüğü değil düştüğü
yer — yoğun saatteki ödünç işlemi. Denetim skor ortalamasına bakıyor, ortalamanın altındaki
dağılıma bakmıyor. **Kaçan ihlal denetimin duyarlılığının altında değil, baktığı büyüklüğün
dışındadır.**

Altı puanlık eşikteki beş yanlış alarmın beşi de bilerek kabul edilmiş takas. Bu, çevrilemeyen
kısmı adıyla ortaya koyar: denetim düşüşün **büyüklüğünü** ölçebiliyor, **kabul edilebilirliğini**
ölçemiyor, çünkü bu bir yargıdır. Yerine muafiyet kaydı konuyor — kabul edilen her düşüş için hangi
niteliğin uğruna kabul edildiği yazılıyor ve denetim kayda bakıyor. Beş kayıttan sonra yanlış alarm
sıfıra iniyor. Bedeli belli: beş kayıt karar her değiştiğinde gözden geçirilmek zorunda, kayıt
eskirse denetim susar ve susması artık doğru değildir.

Son ölçüm denetimin yapısal körlüğünü gösteriyor. Tekil karar denetimi on iki kararda yalnız bir
(karar, yaprak) ikilisinde eşik aşımı görüyor; kararlar birlikte uygulandığında dört yaprak eşiği
aşıyor, üç ayrı nitelikte. Dahası tekil denetimin gördüğü tek aşım birikimde kapanıyor — sonraki
bir karar o yaprağı eşiğin üstüne geri taşıyor. **Tekil karar denetimi ile birikimli denetim aynı
küme üzerinde birbirinin tersini buluyor**; biri seçilirse öteki tamamen kaçar. Maliyet iki
kalemdir: her karardan sonra on dört yaprağın yeniden ölçülmesi (168 ölçüm) ve beş muafiyet kaydının
bakımı; koşum 74 karşılaştırma, ölçümün yanında yok sayılır.

#### Özet

- Gerilim matrisi, bir niteliği yükselten kararın ötekilerde ölçülen düşüşünü sayar; yirmi sıralı
  çiftin 11'i çelişiyor, ikisi karşılıklı.
- En çok bastırılan nitelik sürdürülebilirlik: altı kararda, üç ayrı nitelik uğruna, toplam 53,3 pay
  puanı; hiçbir karar onu hedeflemediği halde bedelini her eklemede o ödüyor.
- Hiçbir karar taşınabilirliği yükseltmiyor, dördü düşürüyor; eşiği yazılı bir nitelik, onu savunan
  karar önerilmediği sürece yalnız düşer.
- Eşik taraması hiçbir eşikte beş ihlali birden yakalamıyor: sekiz puanda 4/1/0, altı puanda 4/1/5
  (yakalanan / kaçan / yanlış alarm); kaçan, denetimin baktığı büyüklüğün dışındadır.
- Denetim düşüşü ölçer, kabul edilebilirliğini ölçemez; yerine beş muafiyet kaydı konuyor ve yanlış
  alarm sıfıra iniyor — tekil denetim bir aşım görürken birikimli denetim dört başka aşım buluyor.

#### Sonraki Adım

Matriste güvenlik dört kararla yükseldi, karşılığında başarımı ve sürdürülebilirliği üçer kararda
bastırdı. Ama bu hesapta güvenlik tek bir skordu: üç yaprağın payının ortalaması. Sonraki ders o
skoru açar, çünkü güvenlik kararının bedeli tek bir sayıya sığmıyor. Sistem, veriyi bir yerden
başkasına geçiren sınırlardan oluşur; kaç sınır olduğu, hangi verinin hangi sınırı geçtiği ve her
sınırda kaç denetim durduğu sayılabilir. Sayıldığında iki şey çıkar: hiçbir denetimden geçmeyen
geçişler ve bir sınır kaldırıldığında açılan yeni yollar.

### Güvenlik Mimarisi

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/guvenlik-mimarisi

Güven sınırlarının grafik olarak sayılması: kaç sınır olduğu, hangi verinin hangi sınırı geçtiği, sınır başına denetim ve denetimsiz geçiş, bir sınırın kaldırılmasının açtığı yol sayısı ve kuralın bilinen güvenlik olaylarında yakaladığı, kaçırdığı, boşa öttüğü durumlar.

Önceki derste güvenlik tek bir skordu — üç yaprağın eşiğe göre payının ortalaması — ve yükseldiğinde
neyin değiştiğini söylemiyordu. Güvenlik kararı bir sayıya değil bir yere konur: verinin bir
bölgeden ötekine geçtiği noktaya.

Kimlik doğrulama ve yetkilendirmenin nasıl gerçekleştirildiği Kimlik Doğrulama ve Yetkilendirme
kursunda, gizli değer yönetimi Sunucu Güvenliği ve Üretime Alma kursunda ölçüldü; burada
tekrarlanmaz. Buradaki soru mimari düzeydedir: kaç güven
sınırı var, hangi veri hangi sınırı geçiyor, her sınırda kaç denetim duruyor, hangi geçişler
hiçbir denetimden geçmiyor. Kursun sorusu da sürer — bu grafik koşturulabilir bir kurala
çevrilebiliyor mu.

#### Güven Sınırı Grafiği

Aşağıdaki blok, kurgusal bölgesel kütüphane ağının güven bölgelerini, sınırları, sınırların
taşıdığı denetimleri, veri türlerinin gerektirdiği denetimleri ve bir çeyrekte gözlenen geçişleri
**modelliyor**. Denetimsiz geçiş, gerekli denetimlerden en az biri geçtiği sınırda bulunmayan
geçiştir.

```js
// sinir.mjs — guven sinirlari grafigini modeller ve sinir basina denetimi sayar
import { mkdirSync, writeFileSync } from "node:fs";

// KN10: kurgusal bolgesel kutuphane aginin yedi guven bolgesi ve aralarindaki sekiz sinir.
// Her sinir, o gecerken uygulanan denetimleri tasir. Model.
const SINIR = [
  ["S1", "uye cihazi", "merkez servis", ["kimlik", "sifreleme"]],
  ["S2", "sube uc birim", "sube agi", ["kimlik"]],
  ["S3", "sube agi", "merkez servis", ["sifreleme", "kayit"]],
  ["S4", "merkez servis", "veri katmani", ["yetki", "sifreleme"]],
  ["S5", "merkez servis", "belediye kimlik", ["kimlik", "sifreleme", "kayit"]],
  ["S6", "merkez servis", "dis katalog", ["sifreleme"]],
  ["S7", "sube agi", "veri katmani", []],
  ["S8", "uye cihazi", "dis katalog", []],
];

// KN11: yedi veri turu ve her turun bir siniri gecerken gerektirdigi denetimler. Katalog
// kaydi acik veridir, denetim gerektirmez. Model.
const GEREKLI = {
  "uye kimligi": ["kimlik", "sifreleme"],
  "odunc gecmisi": ["kimlik", "yetki", "maskeleme"],
  "katalog kaydi": [],
  "ucret kaydi": ["kimlik", "yetki", "kayit"],
  "personel yetkisi": ["kimlik", "yetki", "kayit"],
  "denetim kaydi": ["yetki", "kayit"],
  "sir degeri": ["sifreleme", "kayit"],
};

// KN12: bir ceyrekte gozlenen on sekiz veri gecisi [veri, kaynak bolge, hedef bolge]. Model.
const AKIS = [
  ["uye kimligi", "uye cihazi", "merkez servis"],
  ["uye kimligi", "merkez servis", "belediye kimlik"],
  ["uye kimligi", "sube uc birim", "sube agi"],
  ["odunc gecmisi", "veri katmani", "merkez servis"],
  ["odunc gecmisi", "merkez servis", "uye cihazi"],
  ["odunc gecmisi", "veri katmani", "sube agi"],
  ["odunc gecmisi", "sube agi", "sube uc birim"],
  ["katalog kaydi", "dis katalog", "merkez servis"],
  ["katalog kaydi", "dis katalog", "uye cihazi"],
  ["katalog kaydi", "merkez servis", "veri katmani"],
  ["katalog kaydi", "sube agi", "merkez servis"],
  ["ucret kaydi", "merkez servis", "veri katmani"],
  ["ucret kaydi", "sube uc birim", "sube agi"],
  ["personel yetkisi", "belediye kimlik", "merkez servis"],
  ["personel yetkisi", "merkez servis", "sube agi"],
  ["denetim kaydi", "merkez servis", "veri katmani"],
  ["sir degeri", "merkez servis", "dis katalog"],
  ["sir degeri", "merkez servis", "veri katmani"],
];

const BOLGE = [...new Set(SINIR.flatMap(([, a, b]) => [a, b]))];
const sinirBul = (a, b) => SINIR.find(([, x, y]) => (x === a && y === b) || (x === b && y === a));
const eksik = (veri, s) => GEREKLI[veri].filter((d) => !s[3].includes(d));

mkdirSync("graf", { recursive: true });
writeFileSync("graf/graf.mjs",
  `export const SINIR = ${JSON.stringify(SINIR)};\n` +
  `export const GEREKLI = ${JSON.stringify(GEREKLI)};\n` +
  `export const AKIS = ${JSON.stringify(AKIS)};\n`);

console.log(`${BOLGE.length} guven bolgesi, ${SINIR.length} sinir, ${AKIS.length} veri gecisi, ` +
  `${Object.keys(GEREKLI).length} veri turu`);
console.log(`\n${"sinir".padEnd(6)}${"bolge cifti".padEnd(34)}${"denetim".padStart(8)}` +
  `${"gecis".padStart(7)}${"eksigi olan gecis".padStart(19)}`);
for (const s of SINIR) {
  const gecis = AKIS.filter(([, a, b]) => sinirBul(a, b) === s);
  console.log(`${s[0].padEnd(6)}${`${s[1]} - ${s[2]}`.padEnd(34)}${String(s[3].length).padStart(8)}` +
    `${String(gecis.length).padStart(7)}` +
    `${String(gecis.filter(([v]) => eksik(v, s).length > 0).length).padStart(19)}`);
}

const kusurlu = AKIS.map((a) => [a, sinirBul(a[1], a[2])])
  .map(([a, s]) => [a, s, eksik(a[0], s)]).filter(([, , e]) => e.length > 0);
console.log(`\ndenetimsiz gecis: ${kusurlu.length}/${AKIS.length} gecis en az bir gerekli denetimden gecmiyor`);
console.log(`${"sinir".padEnd(6)}${"veri".padEnd(18)}${"yon".padEnd(34)}eksik denetim`);
for (const [a, s, e] of kusurlu)
  console.log(`${s[0].padEnd(6)}${a[0].padEnd(18)}${`${a[1]} -> ${a[2]}`.padEnd(34)}${e.join(", ")}`);

const hicbiri = kusurlu.filter(([, s]) => s[3].length === 0);
console.log(`\nhicbir denetimi olmayan sinir: ${SINIR.filter((s) => s[3].length === 0).map((s) => s[0]).join(", ")}; ` +
  `bu sinirlardan gecip denetim gerektiren veri sayisi ${hicbiri.length}`);
const turBasina = Object.keys(GEREKLI).map((v) =>
  [v, kusurlu.filter(([a]) => a[0] === v).length]).filter(([, n]) => n > 0);
console.log(`veri turu basina eksigi olan gecis: ` +
  turBasina.map(([v, n]) => `${v} ${n}`).join(", "));
```

```
7 guven bolgesi, 8 sinir, 18 veri gecisi, 7 veri turu

sinir bolge cifti                        denetim  gecis  eksigi olan gecis
S1    uye cihazi - merkez servis               2      2                  1
S2    sube uc birim - sube agi                 1      3                  3
S3    sube agi - merkez servis                 2      2                  1
S4    merkez servis - veri katmani             2      5                  4
S5    merkez servis - belediye kimlik          3      2                  1
S6    merkez servis - dis katalog              1      2                  1
S7    sube agi - veri katmani                  0      1                  1
S8    uye cihazi - dis katalog                 0      1                  0

denetimsiz gecis: 12/18 gecis en az bir gerekli denetimden gecmiyor
sinir veri              yon                               eksik denetim
S2    uye kimligi       sube uc birim -> sube agi         sifreleme
S4    odunc gecmisi     veri katmani -> merkez servis     kimlik, maskeleme
S1    odunc gecmisi     merkez servis -> uye cihazi       yetki, maskeleme
S7    odunc gecmisi     veri katmani -> sube agi          kimlik, yetki, maskeleme
S2    odunc gecmisi     sube agi -> sube uc birim         yetki, maskeleme
S4    ucret kaydi       merkez servis -> veri katmani     kimlik, kayit
S2    ucret kaydi       sube uc birim -> sube agi         yetki, kayit
S5    personel yetkisi  belediye kimlik -> merkez servis  yetki
S3    personel yetkisi  merkez servis -> sube agi         kimlik, yetki
S4    denetim kaydi     merkez servis -> veri katmani     kayit
S6    sir degeri        merkez servis -> dis katalog      kayit
S4    sir degeri        merkez servis -> veri katmani     kayit

hicbir denetimi olmayan sinir: S7, S8; bu sinirlardan gecip denetim gerektiren veri sayisi 1
veri turu basina eksigi olan gecis: uye kimligi 1, odunc gecmisi 4, ucret kaydi 2, personel yetkisi 2, denetim kaydi 1, sir degeri 2
```

On sekiz geçişin on ikisi en az bir gerekli denetimden geçmiyor. Sınır başına dağılım eşit değil:
belediye kimlik servisine bakan sınırda üç denetim var, şube ağı ile veri katmanı arasındakinde
hiç yok. Ama denetimsiz iki sınır aynı şey değil: üye cihazı ile dış katalog arasında da denetim yok,
oradan yalnız açık veri olan katalog kaydı geçiyor. **Denetimsiz sınır tek
başına kusur değildir; kusur, denetimsiz sınırdan denetim gerektiren verinin geçmesidir.**

En yüklü sınır, en çok eksiği olan sınır: merkez servis ile veri katmanı arasından beş geçiş var,
dördünde eksik denetim. Rastlantı değil — bir sınırdan geçen veri türü arttıkça gereken
denetim kümesi genişler, ama sınırdaki denetim kümesi tektir.

Veri türü başına ödünç geçmişi dört eksik geçişle başta: dört ayrı sınırdan geçiyor ve
gerektirdiği üç denetimden biri, maskeleme, sekiz sınırın hiçbirinde tanımlı değil.
**Gerekli denetimlerden biri sistemde hiç yok** ve bu, ancak gerekli küme ile var olan küme yan
yana konduğunda görünüyor.

#### Sınır Kaldırmak ve Kuralın Sınırı

Grafiğin ikinci sorusu: bir sınır kaldırılırsa ne olur. Kaldırmak, iki bölgenin tek güven bölgesi
olması, birindeki verinin koşulsuz ötekine geçmesi demektir. Aşağıdaki blok bunu her sınır için
hesaplıyor, sonra kuralı bilinen olaylarda koşturuyor.

```js
// kaldirma.mjs — sinir kaldirmanin actigi yolu sayar ve kurali bilinen ihlal kumesinde kosar
import { SINIR, GEREKLI, AKIS } from "./graf/graf.mjs";

// KN13: her veri turunun dogdugu bolge; yayilim, kaynaktan baslayip akis oklarini izleyen
// gecisli kapanistir. Model.
const KAYNAK = {
  "uye kimligi": "uye cihazi", "odunc gecmisi": "veri katmani",
  "katalog kaydi": "dis katalog", "ucret kaydi": "sube uc birim",
  "personel yetkisi": "belediye kimlik", "denetim kaydi": "merkez servis",
  "sir degeri": "merkez servis",
};
const VERI = Object.keys(GEREKLI);

// Bir sinir kaldirildiginda iki bolge tek guven bolgesi olur: iki uc arasinda her veri turu
// icin kosulsuz gecis dogar.
const yayilim = (veri, kaldirilan) => {
  const ulasilan = new Set([KAYNAK[veri]]);
  const es = kaldirilan ? [kaldirilan[1], kaldirilan[2]] : [];
  for (let n = 0; n < SINIR.length + 1; n += 1)
    for (const [v, a, b] of AKIS) {
      if (v === veri && ulasilan.has(a)) ulasilan.add(b);
      if (es.length && ulasilan.has(es[0])) ulasilan.add(es[1]);
      if (es.length && ulasilan.has(es[1])) ulasilan.add(es[0]);
    }
  return ulasilan;
};

const temel = Object.fromEntries(VERI.map((v) => [v, yayilim(v, null)]));
const temelToplam = VERI.reduce((s, v) => s + temel[v].size, 0);
console.log(`temel yayilim: ${temelToplam} (veri turu, bolge) cifti, ` +
  `${VERI.length} veri turu x ${new Set(SINIR.flatMap((s) => [s[1], s[2]])).size} bolge icinde`);
console.log(`\n${"sinir".padEnd(6)}${"kaldirilirsa birlesen bolgeler".padEnd(34)}` +
  `${"acilan yol".padStart(11)}  en cok acilan veri`);
const acilan = SINIR.map((s) => {
  const yeni = VERI.map((v) => [v, yayilim(v, s).size - temel[v].size]);
  return [s, yeni.reduce((t, [, n]) => t + n, 0), yeni.sort((a, b) => b[1] - a[1])[0]];
}).sort((a, b) => b[1] - a[1]);
for (const [s, toplam, [v, n]] of acilan)
  console.log(`${s[0].padEnd(6)}${`${s[1]} + ${s[2]}`.padEnd(34)}${String(toplam).padStart(11)}` +
    `  ${n > 0 ? `${v} (${n})` : "-"}`);

// Kural: her gecis, gectigi sinirda veri turunun gerektirdigi denetimlerden gecmeli.
const sinirBul = (a, b) => SINIR.find(([, x, y]) => (x === a && y === b) || (x === b && y === a));
const eksik = (veri, s) => (s ? GEREKLI[veri].filter((d) => !s[3].includes(d)) : null);
const uyari = AKIS.map((a) => [a, sinirBul(a[1], a[2])])
  .filter(([a, s]) => eksik(a[0], s).length > 0);

// KN14: bilinen ihlal kumesi — gecen iki ceyrekte kayda gecmis on guvenlik olayi
// [veri, kaynak, hedef, not]. Model.
const IHLAL = [
  ["odunc gecmisi", "veri katmani", "sube agi", ""],
  ["ucret kaydi", "sube uc birim", "sube agi", ""],
  ["sir degeri", "merkez servis", "dis katalog", ""],
  ["odunc gecmisi", "sube agi", "sube uc birim", ""],
  ["personel yetkisi", "merkez servis", "sube agi", ""],
  ["ucret kaydi", "merkez servis", "veri katmani", ""],
  ["odunc gecmisi", "veri katmani", "veri katmani", "bolge ici yedek kopyasi"],
  ["sir degeri", "merkez servis", "merkez servis", "kayit dosyasina dusen sir"],
  ["uye kimligi", "veri katmani", "dis katalog", "grafikte tanimsiz yol"],
  ["uye kimligi", "uye cihazi", "merkez servis", "denetimleri tam olan sinir"],
];
// KN15: uyarilan gecislerden ucunde eksik denetim sinirda degil baska yerde karsilaniyor.
const KARSILANAN = [["denetim kaydi", "merkez servis", "veri katmani", "veri katmani islem gunlugu"],
  ["sir degeri", "merkez servis", "veri katmani", "veri katmani islem gunlugu"],
  ["personel yetkisi", "belediye kimlik", "merkez servis", "yetki kaynakta imzalaniyor"]];

const es = (a, b) => a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
const yakalanan = IHLAL.filter((i) => uyari.some(([a]) => es(a, i)));
const kacan = IHLAL.filter((i) => !yakalanan.includes(i));
const yanlis = uyari.filter(([a]) => KARSILANAN.some((k) => es(a, k)));
const acikBosluk = uyari.length - yakalanan.length - yanlis.length;
console.log(`\n${uyari.length} uyari, ${IHLAL.length} bilinen olay: yakalanan ${yakalanan.length}, ` +
  `kacan ${kacan.length}, yanlis alarm ${yanlis.length}, ` +
  `henuz olay uretmemis acik bosluk ${acikBosluk}`);
for (const i of kacan) {
  const s = sinirBul(i[1], i[2]);
  console.log(`  kacan: ${i[0].padEnd(16)} ${`${i[1]} -> ${i[2]}`.padEnd(32)} ` +
    `${i[3] || (s ? "sinirda eksik denetim yok" : "sinir yok")}`);
}
for (const [a] of yanlis) {
  const k = KARSILANAN.find((n) => es(a, n));
  console.log(`  yanlis alarm: ${a[0].padEnd(16)} eksik denetim baska yerde: ${k[3]}`);
}

// KN16: cevrilemeyen kisim. Grafik bildirilen sinirlardaki gecisi denetler; sinirin yerini ve
// bildirilmemis yolu denetleyemez. Yerine ceyrekte bir, sinir basina 3 kisi-saatlik gozden
// gecirme konur.
const SAAT = 3;
const grafDisi = kacan.filter((i) => !sinirBul(i[1], i[2])).length;
console.log(`\ncevrilemeyen: sinirin dogru yerde olmasi ve bildirilmemis yol; kacan ` +
  `${kacan.length} olayin ${grafDisi} tanesi grafikte yolu olmayan gecis`);
console.log(`yerine konan: ${SINIR.length} sinir icin gozden gecirme, ` +
  `${SINIR.length * SAAT} kisi-saat/ceyrek; siralamayi kaldirma analizi veriyor ` +
  `(en ustteki ${acilan[0][0][0]}, ${acilan[0][1]} yol)`);
const kayit = SINIR.length + AKIS.length + VERI.length;
console.log(`denetim maliyeti: elle tutulan ${kayit} grafik kaydi (sinir, akis, veri turu); ` +
  `kosum ${AKIS.reduce((s, a) => s + GEREKLI[a[0]].length, 0)} denetim karsilastirmasi + ` +
  `${SINIR.length} kaldirma hesabi; grafik guncellenmezse kacan artar`);
```

```
temel yayilim: 22 (veri turu, bolge) cifti, 7 veri turu x 7 bolge icinde

sinir kaldirilirsa birlesen bolgeler     acilan yol  en cok acilan veri
S3    sube agi + merkez servis                    6  ucret kaydi (2)
S7    sube agi + veri katmani                     5  katalog kaydi (1)
S5    merkez servis + belediye kimlik             4  odunc gecmisi (1)
S6    merkez servis + dis katalog                 4  uye kimligi (1)
S1    uye cihazi + merkez servis                  3  personel yetkisi (1)
S8    uye cihazi + dis katalog                    3  uye kimligi (1)
S4    merkez servis + veri katmani                2  uye kimligi (1)
S2    sube uc birim + sube agi                    1  personel yetkisi (1)

12 uyari, 10 bilinen olay: yakalanan 6, kacan 4, yanlis alarm 3, henuz olay uretmemis acik bosluk 3
  kacan: odunc gecmisi    veri katmani -> veri katmani     bolge ici yedek kopyasi
  kacan: sir degeri       merkez servis -> merkez servis   kayit dosyasina dusen sir
  kacan: uye kimligi      veri katmani -> dis katalog      grafikte tanimsiz yol
  kacan: uye kimligi      uye cihazi -> merkez servis      denetimleri tam olan sinir
  yanlis alarm: personel yetkisi eksik denetim baska yerde: yetki kaynakta imzalaniyor
  yanlis alarm: denetim kaydi    eksik denetim baska yerde: veri katmani islem gunlugu
  yanlis alarm: sir degeri       eksik denetim baska yerde: veri katmani islem gunlugu

cevrilemeyen: sinirin dogru yerde olmasi ve bildirilmemis yol; kacan 4 olayin 3 tanesi grafikte yolu olmayan gecis
yerine konan: 8 sinir icin gozden gecirme, 24 kisi-saat/ceyrek; siralamayi kaldirma analizi veriyor (en ustteki S3, 6 yol)
denetim maliyeti: elle tutulan 33 grafik kaydi (sinir, akis, veri turu); kosum 36 denetim karsilastirmasi + 8 kaldirma hesabi; grafik guncellenmezse kacan artar
```

Sınırlar eşit değerde değil. Şube ağı ile merkez servis arasındaki sınır kaldırılırsa altı yeni
yol açılıyor, şube uç birimi ile şube ağı arasındaki kaldırılırsa bir. Sıralama sınırdaki denetim sayısıyla
örtüşmüyor: iki denetim taşıyan sınır, hiç denetim taşımayandan daha çok yol açıyor. **Bir sınırın
değeri taşıdığı denetimle değil, ayırdığı bölgelerin grafiğe bağlanma biçimiyle ölçülüyor.**

Kural on iki uyarı üretiyor, kayıtta on olay var: altısı yakalanıyor, dördü kaçıyor, üçü yanlış
alarm. Kalan üç uyarı ikisinden de değil — eksik denetim gerçekten eksik, henüz olay üretmemiş. **Olay üretmemiş uyarı yanlış alarm değildir**; yanlış alarm, denetimin gerçekte
karşılandığı halde eksik göründüğü uyarıdır. Ayrım yapılmazsa uyarıların yarısı boşa sayılır ve
kural gevşetilir.

Kaçan dört olayın nedenleri ayrı okunmalı. İkisi bölge içinde kaldı — yedek kopyası ve kayıt
dosyası hiçbir sınırı geçmedi. Biri grafikte tanımsız bir yoldan geçti. Dördüncüsü en
öğreticisi: denetimleri tam olan bir sınırda gerçekleşti. **Grafik denetimin var olup olmadığını
görür, doğru işleyip işlemediğini görmez.** Çevrilemeyen kısım buradan çıkıyor: sınırın doğru yerde
olması ve bildirilmemiş yolun bulunmaması denetlenemiyor, kaçanların üçü bu kapsamda. Yerine sekiz
sınır için çeyrekte bir gözden geçirme konuyor, 24 kişi-saat; sırasını kaldırma analizi veriyor.

Maliyetin ağırlığı koşumda değil: kural otuz altı karşılaştırma ile sekiz kaldırma hesabı, elle
tutulan otuz üç grafik kaydı her mimari değişiklikte güncellenmek zorunda. Grafik güncellenmezse
kural sessizce yanlışlanır: grafikte olmayan bir akıştaki her olay kaçan olur.

#### Özet

- Güven sınırı grafiği yedi bölge, sekiz sınır, on sekiz geçiş; on ikisi en az bir gerekli
  denetimden geçmiyor.
- Denetimsiz sınır tek başına kusur değildir: iki denetimsiz sınırdan yalnız birinden denetim
  gerektiren veri geçiyor; en yüklü sınır en çok eksiği olan sınır.
- Gerekli denetimlerden biri (maskeleme) sekiz sınırın hiçbirinde tanımlı değil; ancak gerekli
  küme ile var olan küme yan yana konduğunda görünüyor.
- Bir sınırın kaldırılması 1 ile 6 arasında yeni yol açıyor; sıralamayı denetim sayısı değil,
  bölgelerin grafiğe bağlanma biçimi belirliyor.
- On iki uyarı, on olay: 6 yakalanan, 4 kaçan, 3 yanlış alarm, 3 açık boşluk; kaçanların üçü
  grafikte yolu olmayan geçiş, biri denetimi tam sınırda.

#### Sonraki Adım

Sınır grafiği hangi bölgenin hangisinden ayrıldığını söyledi, kaç kopyası olduğunu söylemedi. Sistem büyüdüğünde asıl soru şudur: **neyin kopyalanacağı**. Sonraki ders
üç ölçek birimi adayını yan yana koyar — tüm sistemin kopyalanması, tek tek servislerin
kopyalanması, kiracı başına kopyalama — ve her aday için kopyalanan bileşeni, paylaşılan kalan
kaynağı ve hiçbir biçimde kopyalanamayan tekil noktayı sayar.

### Ölçeklenebilirlik Kararları

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/olceklenebilirlik-kararlari

Ölçek biriminin sınırının nereye çizileceği: tüm sistem, servis ve kiracı adayları için kopyalanan bileşenin, paylaşılan kalan kaynağın ve kopyalanamayan tekil noktanın sayılması ve kopyalama kuralının geçmiş ölçekleme denemelerinde yakaladığı, kaçırdığı, boşa öttüğü durumlar.

Güven sınırı grafiği bölgeleri birbirinden ayırdı, ama her bölgenin kaç kopyasının koştuğunu
söylemedi. Sistem büyüdüğünde bu soru öne çıkar ve genellikle yanlış biçimde sorulur. Kaç kopya
gerektiği ve kapasitenin nasıl hesaplanacağı Başarım Karşıt Kalıpları ve İzleme kursunun Kapasite
Planlama dersinde ölçüldü — düğüm sayısı, yük kısıtı ile arıza kısıtının büyüğüdür; burada
tekrarlanmaz. Buradaki soru aritmetiğin öncesindedir ve tek cümledir:
**neyin kopyalanacağı.**

Kopyalanan bütüne ölçek birimi denir. Terim, dağıtım damgası dersindeki terimin aynısıdır; oradaki
soru birimin kaç kopyasının gerektiğiydi, buradaki soru birimin sınırının nereye çizileceği. Sınır
farklı yerlere çizilebilir ve her çizim başka bir şeyi kopyalar, başka bir şeyi paylaşımda bırakır.
Bu ders üç adayı yan yana koyup üç sayıyı çıkarır: kopyalanan bileşen, paylaşılan kalan kaynak,
kopyalanamayan tekil nokta.

#### Üç Ölçek Birimi Adayı

Aşağıdaki blok, kurgusal bölgesel kütüphane ağının bileşenlerini, aralarındaki bağımlılıkları ve
üç ölçek birimi adayını **modelliyor**. Ağ birden çok belediyeye hizmet veriyor; kiracı, bir
belediyenin bütün şubeleri demek. Her aday, hangi bileşenin kopyalanacağını seçen bir kuraldır.

```js
// birim.mjs — uc olcek birimi adayini modeller ve kopyalanani, paylasilani, tekili sayar
import { mkdirSync, writeFileSync } from "node:fs";

// KN17: kurgusal bolgesel kutuphane aginin on dort bileseni. Ag birden cok belediyeye hizmet
// veriyor; kiraci, bir belediyenin butun subeleri demek. Alanlar: [kod, ad, durum,
// kiraciya bolunur mu, kopyalanamama nedeni]. Model.
const BILESEN = [
  ["C1", "odunc servisi", "durumsuz", true, ""],
  ["C2", "ucretlendirme servisi", "durumsuz", true, ""],
  ["C3", "uyelik servisi", "durumsuz", true, ""],
  ["C4", "bildirim gonderici", "durumsuz", true, ""],
  ["C5", "yonetim konsolu", "durumsuz", false, ""],
  ["C6", "arama dizini", "durumlu", true, ""],
  ["C7", "katalog onbellegi", "durumlu", false, ""],
  ["C8", "odunc veritabani", "durumlu", true, ""],
  ["C9", "uyelik veritabani", "durumlu", true, ""],
  ["C10", "ucret defteri", "durumlu", true, ""],
  ["C11", "dis katalog baglayicisi", "durumsuz", false, "tek dis sozlesme"],
  ["C12", "kimlik baglayicisi", "durumsuz", false, "tek dis sozlesme"],
  ["C13", "numara ureteci", "durumlu", false, "kuresel sira"],
  ["C14", "denetim kaydi deposu", "durumlu", false, "tek yazici yukumlulugu"],
];

// KN18: bilesenler arasindaki on sekiz bagimlilik [kaynak, hedef, tur]. Model.
const BAGIMLILIK = [
  ["C1", "C8", "yazar"], ["C1", "C9", "okur"], ["C1", "C13", "yazar"], ["C1", "C14", "yazar"],
  ["C1", "C7", "okur"], ["C2", "C10", "yazar"], ["C2", "C8", "okur"], ["C2", "C14", "yazar"],
  ["C3", "C9", "yazar"], ["C3", "C12", "okur"], ["C3", "C14", "yazar"], ["C4", "C8", "okur"],
  ["C4", "C9", "okur"], ["C5", "C14", "okur"], ["C5", "C10", "okur"], ["C6", "C7", "okur"],
  ["C11", "C7", "yazar"], ["C11", "C6", "yazar"],
];

// Olcek birimi, es zamanli olarak birden cok kopyasi kosturulan butundur. Terim, dagitim
// damgasi dersindekiyle aynidir; orada birimin kac kopyasinin gerektigi hesaplaniyordu,
// burada birimin sinirinin nereye cizilecegi sorusu var.
const ADAY = {
  "tum sistem": (b) => b[4] === "",
  servis: (b) => b[4] === "" && b[2] === "durumsuz",
  kiraci: (b) => b[4] === "" && b[3],
};
const tekil = BILESEN.filter((b) => b[4] !== "");
const bul = (k) => BILESEN.find((b) => b[0] === k);

mkdirSync("model", { recursive: true });
writeFileSync("model/model.mjs",
  `export const BILESEN = ${JSON.stringify(BILESEN)};\n` +
  `export const BAGIMLILIK = ${JSON.stringify(BAGIMLILIK)};\n` +
  `export const ADAY = ${JSON.stringify(Object.keys(ADAY))};\n`);

console.log(`${BILESEN.length} bilesen, ${BAGIMLILIK.length} bagimlilik, ` +
  `${BAGIMLILIK.filter((e) => e[2] === "yazar").length} tanesi yazma`);
console.log(`\n${"olcek birimi adayi".padEnd(20)}${"kopyalanan".padStart(11)}` +
  `${"paylasilan".padStart(11)}${"tekil".padStart(7)}${"paylasilana giren bagimlilik".padStart(29)}`);
for (const [ad, secim] of Object.entries(ADAY)) {
  const kopya = BILESEN.filter(secim);
  const paylasilan = BILESEN.filter((b) => b[4] === "" && !secim(b));
  const giren = BAGIMLILIK.filter(([, h]) => paylasilan.some((p) => p[0] === h)).length;
  console.log(`${ad.padEnd(20)}${String(kopya.length).padStart(11)}` +
    `${String(paylasilan.length).padStart(11)}${String(tekil.length).padStart(7)}` +
    `${String(giren).padStart(29)}`);
}

console.log(`\nhicbir adayda kopyalanamayan tekil nokta (${tekil.length}):`);
for (const b of tekil) console.log(`  ${b[0].padEnd(4)}${b[1].padEnd(26)}${b[4]}`);
const tekileGiden = BAGIMLILIK.filter(([, h]) => tekil.some((t) => t[0] === h));
console.log(`tekil noktalara giden bagimlilik ${tekileGiden.length}, ` +
  `bunlarin ${tekileGiden.filter((e) => e[2] === "yazar").length} tanesi yazma; ` +
  `yazan bilesenler: ${[...new Set(tekileGiden.filter((e) => e[2] === "yazar")
    .map((e) => bul(e[0])[1]))].join(", ")}`);
```

```
14 bilesen, 18 bagimlilik, 9 tanesi yazma

olcek birimi adayi   kopyalanan paylasilan  tekil paylasilana giren bagimlilik
tum sistem                   10          0      4                            0
servis                        5          5      4                           12
kiraci                        8          2      4                            3

hicbir adayda kopyalanamayan tekil nokta (4):
  C11 dis katalog baglayicisi   tek dis sozlesme
  C12 kimlik baglayicisi        tek dis sozlesme
  C13 numara ureteci            kuresel sira
  C14 denetim kaydi deposu      tek yazici yukumlulugu
tekil noktalara giden bagimlilik 6, bunlarin 4 tanesi yazma; yazan bilesenler: odunc servisi, ucretlendirme servisi, uyelik servisi
```

Üç adayın kopyalanan sütunu birbirinden farklı: tüm sistem on bileşen kopyalıyor, kiracı sekiz,
servis beş. Ama tekil sütunu üç adayda da dört. **Ölçek birimini değiştirmek tekil noktaları
değiştirmiyor.** Sınır nereye çizilirse çizilsin dört bileşen tek kopya kalıyor, çünkü bunların
kopyalanamama nedeni birimin sınırıyla ilgili değil: ikisi tek dış sözleşmeye bağlı, biri küresel
bir sıra üretiyor, biri tek yazıcı yükümlülüğü taşıyor. Son ikisi teknik kısıt, ilk ikisi değil.
Sözleşmeden gelen tekillik bir mimari kararla çözülmez; başka bir kararla, sözleşmeyi değiştirerek
çözülür ve bu ölçek birimi tartışmasının dışındadır.

Asıl bedeli son sütun gösteriyor. Servis adayı en az bileşeni kopyalıyor ve paylaşımda kalan
kaynağa giren on iki bağımlılık bırakıyor; kiracı adayı üç, tüm sistem adayı sıfır bırakıyor.
**Kopyalanan bileşen sayısı ile paylaşımda kalan yük ters yönde hareket ediyor** ve bu ilişki
adayların gerçek karşılaştırmasıdır: servis adayı ucuz kopyalanır ama on iki bağımlılığı ortak
kaynağa dayar; her biri kopyalar arttıkça darboğaz adayıdır.

Tüm sistem adayının sıfırı dikkatli okunmalı. Sıfır, paylaşımın kalmadığı anlamına gelmiyor; o
adayda paylaşılan sütunu boş çünkü paylaşılan her şey tekil sütununa geçti. Dört tekil noktaya
giden altı bağımlılığın dördü yazma ve yazanlar üç ayrı servis. Yani hangi aday seçilirse seçilsin,
üç servis tek kopya kalan bir bileşene yazmaya devam ediyor. Ölçek birimi seçimi bu üç yazmayı
ortadan kaldırmıyor, yalnız hangi sınırın içinde göründüklerini değiştiriyor.

#### Kopyalama Kuralı

Üç sütun bir tablo doldurur; sıradaki soru bunun koşturulabilir bir kurala çevrilip
çevrilemeyeceğidir. Kural şöyle yazılabilir: bir ölçek biriminin içindeki bileşen, birimin
dışındaki durumlu bir bileşene yazmamalı. Yazıyorsa birimin iki kopyası aynı duruma eş zamanlı
dokunur. Aşağıdaki blok bu kuralı yazıyor ve geçen yıl denenmiş on ölçekleme adımında koşturuyor.

```js
// denetim.mjs — olcek birimi kuralini yazar ve gecmis olcekleme denemelerinde kosturur
import { BILESEN, BAGIMLILIK } from "./model/model.mjs";

const bul = (k) => BILESEN.find((b) => b[0] === k);
const SECIM = {
  "tum sistem": (b) => b[4] === "",
  servis: (b) => b[4] === "" && b[2] === "durumsuz",
  kiraci: (b) => b[4] === "" && b[3],
};
// Servis adayinda birim tek bir servistir; oteki iki adayda birim, adayin sectigi butun
// bilesenlerdir. Kural: birim icindeki bir bilesen, birim disindaki durumlu bir bilesene
// yazmamali. Yazarsa kopyalar ayni duruma es zamanli dokunur.
const birim = (aday, bilesen) =>
  (aday === "servis" ? [bilesen] : BILESEN.filter(SECIM[aday]).map((b) => b[0]));
const ihlalKenari = (u) => BAGIMLILIK.filter(([k, h, t]) =>
  t === "yazar" && u.includes(k) && !u.includes(h) && bul(h)[2] === "durumlu");

// KN19: bilinen ihlal kumesi — gecen bir yilda denenmis on olcekleme adimi ve gercek sonucu
// [aday, bilesen, sonuc, not]. Model.
const DENEME = [
  ["servis", "C1", "sorun", "kuresel odunc numarasina iki kopyadan yazma"],
  ["servis", "C2", "sorun", "ucret defterine cift kayit"],
  ["servis", "C3", "sorun", "uyelik veritabanina cift yazma"],
  ["servis", "C4", "sorunsuz", "yalniz okuyor"],
  ["servis", "C5", "sorunsuz", "yalniz okuyor"],
  ["servis", "C6", "sorunsuz", "dizin kopya basina yeniden uretiliyor"],
  ["servis", "C7", "sorun", "iki kopya ayni yerel disk yolunu paylasiyor"],
  ["servis", "C11", "sorun", "tek dis sozlesme iki kopyayi kaldirmiyor"],
  ["kiraci", "", "sorunsuz", "yazmalar kiraci anahtarina bolunmus, denetim kaydi eklemeli"],
  ["tum sistem", "", "sorun", "denetim kaydi deposuna iki damgadan yazma"],
];

console.log(`${"aday".padEnd(12)}${"birim".padEnd(26)}${"kural".padEnd(11)}` +
  `${"gercek".padEnd(10)}sonuc`);
let yakalanan = 0, kacan = 0, yanlis = 0, sessizlik = 0;
for (const [aday, kod, gercek, not] of DENEME) {
  const u = birim(aday, kod);
  const kenar = ihlalKenari(u);
  const oter = kenar.length > 0;
  const sonuc = oter && gercek === "sorun" ? "yakalanan"
    : oter ? "yanlis alarm" : gercek === "sorun" ? "kacan" : "dogru sessizlik";
  if (sonuc === "yakalanan") yakalanan += 1;
  else if (sonuc === "kacan") kacan += 1;
  else if (sonuc === "yanlis alarm") yanlis += 1;
  else sessizlik += 1;
  console.log(`${aday.padEnd(12)}${(kod ? bul(kod)[1] : "adayin tumu").padEnd(26)}` +
    `${(oter ? `oter (${kenar.length})` : "susar").padEnd(11)}${gercek.padEnd(10)}${sonuc}`);
}
console.log(`\n${DENEME.length} deneme, ${DENEME.filter((d) => d[2] === "sorun").length} sorunlu: ` +
  `yakalanan ${yakalanan}, kacan ${kacan}, yanlis alarm ${yanlis}, dogru sessizlik ${sessizlik}`);
for (const d of DENEME) {
  const oter = ihlalKenari(birim(d[0], d[1])).length > 0;
  if ((oter && d[2] === "sorunsuz") || (!oter && d[2] === "sorun"))
    console.log(`  ${oter ? "yanlis alarm" : "kacan"}: ${d[0]}/${d[1] ? bul(d[1])[1] : "tumu"} — ${d[3]}`);
}

// KN20: cevrilemeyen kisim. Kural bildirilen bagimliliklara bakar; iki kopyanin bildirilmeyen
// bir kaynagi (ayni disk yolu, ayni surec, ayni zamanlayici) paylasmasini goremez. Yerine
// kopyalama sinamasi konur: iki kopya yan yana kosturulup cakisma gozlenir, deneme basina
// 6 kisi-saat. Sinama, kacan denemeyi kopyalar acilir acilmaz gosterir.
const SAAT = 6;
console.log(`\ncevrilemeyen: bildirilmeyen paylasilan kaynak; kacan ${kacan} denemenin nedeni bu`);
console.log(`yerine konan: kopyalama sinamasi, ${DENEME.length} deneme x ${SAAT} = ` +
  `${DENEME.length * SAAT} kisi-saat; sinama kacani yakalar, yanlis alarmi da ayirir`);
const alan = BILESEN.length * 5 + BAGIMLILIK.length * 3;
console.log(`kural maliyeti: elle bildirilen ${alan} alan (${BILESEN.length} bilesen x 5, ` +
  `${BAGIMLILIK.length} bagimlilik x 3); kosum ${DENEME.length * BAGIMLILIK.length} kenar ` +
  `karsilastirmasi; bildirim eskirse kacan artar`);
```

```
aday        birim                     kural      gercek    sonuc
servis      odunc servisi             oter (3)   sorun     yakalanan
servis      ucretlendirme servisi     oter (2)   sorun     yakalanan
servis      uyelik servisi            oter (2)   sorun     yakalanan
servis      bildirim gonderici        susar      sorunsuz  dogru sessizlik
servis      yonetim konsolu           susar      sorunsuz  dogru sessizlik
servis      arama dizini              susar      sorunsuz  dogru sessizlik
servis      katalog onbellegi         susar      sorun     kacan
servis      dis katalog baglayicisi   oter (2)   sorun     yakalanan
kiraci      adayin tumu               oter (4)   sorunsuz  yanlis alarm
tum sistem  adayin tumu               oter (4)   sorun     yakalanan

10 deneme, 6 sorunlu: yakalanan 5, kacan 1, yanlis alarm 1, dogru sessizlik 3
  kacan: servis/katalog onbellegi — iki kopya ayni yerel disk yolunu paylasiyor
  yanlis alarm: kiraci/tumu — yazmalar kiraci anahtarina bolunmus, denetim kaydi eklemeli

cevrilemeyen: bildirilmeyen paylasilan kaynak; kacan 1 denemenin nedeni bu
yerine konan: kopyalama sinamasi, 10 deneme x 6 = 60 kisi-saat; sinama kacani yakalar, yanlis alarmi da ayirir
kural maliyeti: elle bildirilen 124 alan (14 bilesen x 5, 18 bagimlilik x 3); kosum 180 kenar karsilastirmasi; bildirim eskirse kacan artar
```

On denemenin altısı sorun çıkarmıştı. Kural beşini yakalıyor, birini kaçırıyor, bir kez boşa ötüyor
ve üç kez doğru susuyor. Doğru sessizlik ayrı sayılıyor çünkü bir kuralın değeri yalnız
ateşlemelerinde değil: bildirim göndericisi, yönetim konsolu ve arama dizini denemelerinde kural
sustu ve üçünde de sorun çıkmadı. Yalnız ateşlemeler sayılsaydı kuralın isabeti beş bölü altı
görünürdü; sessizlikler de sayıldığında sekiz bölü on.

Kaçan deneme katalog önbelleği. İki kopya aynı yerel disk yolunu paylaştığı için çakıştılar, ama
bu paylaşım bağımlılık listesinde yok — çünkü liste bileşenler arası bağımlılıkları taşıyor, bir
bileşenin ortamıyla kurduğu bağı değil. **Kural bildirilen bağımlılıklara bakar; bildirilmeyen
paylaşım kuralın dışındadır** ve bu kuralın duyarlılığıyla değil kapsamıyla ilgilidir.

Yanlış alarm kiracı adayında çıkıyor. Kural doğru bir şey görüyor: kiracı birimi içindeki
bileşenler birim dışındaki dört durumlu noktaya yazıyor. Ama yazmaların hepsi kiracı anahtarına
bölünmüş ya da denetim kaydına eklemeli, yani iki kopya aynı satıra dokunmuyor. **Kural yazmanın
varlığını görüyor, yazmanın biçimini görmüyor.** Bölünmüş yazma ile çakışan yazmayı ayırmak için
bağımlılık kaydına bir alan daha eklemek gerekir ve bu alan her bağımlılık için elle doldurulur.

Bir de doğru sonucun yanlış gerekçeyle geldiği bir satır var. Dış katalog bağlayıcısı denemesinde
kural öttü ve deneme gerçekten sorun çıkardı, ama nedeni kuralın gösterdiği yazma kenarı değil, tek
dış sözleşmeydi. Kuralın işaret ettiği kenar düzeltilseydi sorun aynen sürerdi. **Yakalanan sayısı,
gerekçenin doğruluğunu ölçmez**; bu ayrım ancak her yakalamanın nedeni okunduğunda görünür.

Kuralın çevrilemeyen kısmı bildirilmeyen paylaşılan kaynaktır ve kaçan tek denemenin nedeni tam
olarak budur. Yerine kopyalama sınaması konuyor: iki kopya yan yana koşturulup çakışma gözleniyor,
deneme başına altı kişi-saat, on denemede 60 kişi-saat. Sınama kaçanı kopyalar açılır açılmaz
gösterir ve yanlış alarmı da ayırır, çünkü bölünmüş yazmada çakışma gözlenmez. Kuralın kendi
maliyeti başka bir kalemdir: on dört bileşen ve on sekiz bağımlılık için elle bildirilen 124 alan.
Koşum 180 kenar karşılaştırması, yani ölçülecek bir bedel taşımıyor; bildirimin eskimesi ise
doğrudan kaçana dönüşüyor.

#### Özet

- Ölçek birimi, eş zamanlı olarak birden çok kopyası koşan bütündür; bu derste sorulan, birimin
  kaç kopyasının gerektiği değil sınırının nereye çizileceğidir.
- Üç aday kopyalananda ayrışıyor (tüm sistem 10, kiracı 8, servis 5) ama tekilde ayrışmıyor: üçünde
  de dört bileşen kopyalanamıyor, ikisinin nedeni teknik değil sözleşmeye bağlı.
- Kopyalanan ile paylaşımda kalan ters yönde hareket ediyor: servis adayı en az kopyalayıp ortak
  kaynağa giren on iki bağımlılık bırakıyor, kiracı üç, tüm sistem sıfır.
- Kopyalama kuralı on denemede 5 yakalanan, 1 kaçan, 1 yanlış alarm, 3 doğru sessizlik veriyor;
  kaçanın nedeni duyarlılık değil kapsam, yanlış alarmın nedeni yazmanın biçiminin görülmemesi.
- Bir yakalamada gerekçe yanlış: kural yazma kenarını gösterdi, sorunun nedeni tek dış sözleşmeydi;
  çevrilemeyen kısmın yerine konan kopyalama sınaması on denemede 60 kişi-saat.

#### Sonraki Adım

Buraya kadar sayılan her şey — nitelik ağacı, gerilim matrisi, güven sınırı grafiği, ölçek birimi —
sistemi değiştirmeyi gerektiriyor. Değiştirmenin kendisi hiç ölçülmedi. Sonraki ders aynı değişiklik
kümesini iki ayrı yapı üzerinde koşturur ve değişiklik başına dokunulan dosyayı, dokunulan modülü
ve değişikliğin kaç adım öteye yayıldığını sayar. Sonra ölçütlerin kendisini sınar: bu sayılar
zaman içinde bozulmayı gösteriyor mu, yoksa yapı kötüleşirken sabit mi kalıyorlar.

### Sürdürülebilirlik Ölçütleri

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/surdurulebilirlik-olcutleri

Değişim maliyetinin doğrudan gözlenmesi: aynı on iki değişikliğin iki yapıda koşturulup değişiklik başına dokunulan dosya, dokunulan modül ve yayılma derinliğinin sayılması, altı çeyreklik bozulmada ölçütlerin bozulmayı gösterip göstermediğinin sınanması ve eşik kuralının gerçek maliyet kaydında yakaladığı, kaçırdığı, boşa öttüğü durumlar.

Bu konudaki her ölçü bir değişiklik önerdi. Nitelik ağacı eksik denetimleri gösterdi, gerilim
matrisi hangi kararın neyi bastırdığını, güven sınırı grafiği hangi geçişin denetimsiz olduğunu,
ölçek birimi neyin kopyalanacağını. Hepsinin ortak varsayımı, sistemin değiştirilebilir olduğuydu
ve bu varsayım hiç ölçülmedi. Sürdürülebilirlik ağaçta bir kök nitelikti, gerilim matrisinde en çok
bastırılan nitelikti; ama iki yerde de yaprak değerlerinden dolaylı olarak okundu. Bu ders onu
doğrudan ölçer: aynı değişiklik kümesi iki ayrı yapı üzerinde koşturulur.

Teknik borcun faizi Mimari Karar ve Dokümantasyon kursunda, karar kaydı konusunda ölçüldü ve
burada tekrarlanmaz. Değişim maliyeti, o faizin
gözlenen yüzüdür — faiz bir hesap kalemi olarak değil, her değişiklikte fazladan dokunulan dosya
olarak görünür. Ölçülecek üç büyüklük var: değişiklik başına dokunulan dosya, dokunulan modül ve
değişikliğin kaç adım öteye yayıldığı.

#### İki Yapıda Aynı Değişiklik

Aşağıdaki blok iki yapıyı **modelliyor**. Modül kümesi ikisinde de aynı; ayrılan iki şey var —
bağımlılık grafiği ve kavramların modüllere yerleşimi. Aynı on iki değişiklik isteği iki yapıda da
koşturuluyor. Yüzeyi değiştiren bir istek, dokunulan modüle bağımlı modülleri de dokundurur.

```js
// yapi.mjs — ayni degisiklik kumesini iki yapida kosturur ve degisim maliyetini olcer
import { mkdirSync, writeFileSync } from "node:fs";

// KN25: kurgusal bolgesel kutuphane aginin ayni islevi tasiyan iki yapisi. Modul kumesi ayni,
// bagimlilik grafigi ve kavramlarin yerlesimi farkli. Model.
const MODUL = ["odunc", "ucret", "uyelik", "katalog", "bildirim", "rapor", "sube", "ortak"];
const BAGIMLILIK = {
  A: [["odunc", "ortak"], ["odunc", "ucret"], ["odunc", "uyelik"], ["ucret", "ortak"],
    ["ucret", "uyelik"], ["uyelik", "ortak"], ["katalog", "ortak"], ["bildirim", "odunc"],
    ["bildirim", "uyelik"], ["rapor", "odunc"], ["rapor", "ucret"], ["rapor", "uyelik"],
    ["sube", "odunc"], ["sube", "ucret"], ["sube", "katalog"]],
  B: [["odunc", "ortak"], ["ucret", "ortak"], ["uyelik", "ortak"], ["katalog", "ortak"],
    ["bildirim", "ortak"], ["rapor", "ortak"], ["sube", "ortak"], ["rapor", "odunc"],
    ["sube", "odunc"]],
};

// KN26: on iki degisiklik istegi. Her istek bir kavrama dokunur; kavramin hangi modulde kac
// dosyada gectigi yapiya gore degisir. yuzey = degisiklik modulun disa acik yuzeyini
// degistiriyor mu (ayni istek icin iki yapida da ayni). Model.
const DEGISIKLIK = [
  ["ucret tarifesi", true, { ucret: 2, odunc: 1, sube: 1, rapor: 1 }, { ucret: 2 }],
  ["gecikme esigi", false, { ucret: 1, bildirim: 1, odunc: 1 }, { ucret: 1 }],
  ["uye alani", true, { uyelik: 2, odunc: 1, rapor: 1, sube: 1 }, { uyelik: 2 }],
  ["odunc suresi", false, { odunc: 2, ucret: 1, bildirim: 1 }, { odunc: 2 }],
  ["rezervasyon kurali", true, { odunc: 2, sube: 1 }, { odunc: 2 }],
  ["katalog alani", true, { katalog: 2, sube: 1, rapor: 1 }, { katalog: 2 }],
  ["bildirim metni", false, { bildirim: 2 }, { bildirim: 2 }],
  ["sube takvimi", false, { sube: 2, odunc: 1 }, { sube: 2 }],
  ["rapor sutunu", false, { rapor: 2, ucret: 1 }, { rapor: 2 }],
  ["para birimi", true, { ortak: 1, ucret: 2, rapor: 1, sube: 1 }, { ortak: 2 }],
  ["kimlik alani", true, { uyelik: 2, ortak: 1, odunc: 1 }, { uyelik: 2 }],
  ["indirim kurali", false, { ucret: 2, odunc: 1, rapor: 1 }, { ucret: 2 }],
];

// Yayilma: yuzeyi degistiren bir degisiklik, dokunulan module bagimli modulleri de
// dokundurur; her dolayli modulde bir cagri yeri duzeltilir. Derinlik, dogrudan dokunulan
// modulden en uzak dolayli module kadar olan adim sayisidir.
const olc = (yapi, yuzey, yerlesim) => {
  const dogrudan = Object.keys(yerlesim);
  let dosya = Object.values(yerlesim).reduce((s, n) => s + n, 0);
  const gorulen = new Map(dogrudan.map((m) => [m, 0]));
  if (yuzey) {
    let sira = [...dogrudan], derinlik = 0;
    while (sira.length) {
      derinlik += 1;
      const sonraki = [];
      for (const m of sira)
        for (const [a, b] of BAGIMLILIK[yapi])
          if (b === m && !gorulen.has(a)) { gorulen.set(a, derinlik); sonraki.push(a); dosya += 1; }
      sira = sonraki;
    }
  }
  return { dosya, modul: gorulen.size, derinlik: Math.max(...gorulen.values()) };
};

mkdirSync("model", { recursive: true });
writeFileSync("model/model.mjs",
  `export const MODUL = ${JSON.stringify(MODUL)};\n` +
  `export const BAGIMLILIK = ${JSON.stringify(BAGIMLILIK)};\n` +
  `export const DEGISIKLIK = ${JSON.stringify(DEGISIKLIK)};\n` +
  `export const olc = ${olc};\n`);

console.log(`${MODUL.length} modul; bagimlilik A ${BAGIMLILIK.A.length}, B ${BAGIMLILIK.B.length}`);
console.log(`\n${"degisiklik".padEnd(20)}${"yuzey".padEnd(7)}` +
  `${"A dosya".padStart(8)}${"A modul".padStart(8)}${"A der".padStart(6)}` +
  `${"B dosya".padStart(9)}${"B modul".padStart(8)}${"B der".padStart(6)}`);
const olcum = DEGISIKLIK.map(([ad, yuzey, a, b]) =>
  [ad, yuzey, olc("A", yuzey, a), olc("B", yuzey, b)]);
for (const [ad, yuzey, a, b] of olcum)
  console.log(`${ad.padEnd(20)}${(yuzey ? "evet" : "hayir").padEnd(7)}` +
    `${String(a.dosya).padStart(8)}${String(a.modul).padStart(8)}${String(a.derinlik).padStart(6)}` +
    `${String(b.dosya).padStart(9)}${String(b.modul).padStart(8)}${String(b.derinlik).padStart(6)}`);

const ort = (yapi, alan) =>
  (olcum.reduce((s, o) => s + o[yapi][alan], 0) / olcum.length).toFixed(2);
console.log(`\ndegisiklik basina ortalama — A: dosya ${ort(2, "dosya")}, modul ${ort(2, "modul")}, ` +
  `derinlik ${ort(2, "derinlik")}`);
console.log(`degisiklik basina ortalama — B: dosya ${ort(3, "dosya")}, modul ${ort(3, "modul")}, ` +
  `derinlik ${ort(3, "derinlik")}`);
const enKotu = olcum.slice().sort((a, b) => b[3].modul - a[3].modul)[0];
console.log(`iki yapinin da en pahali degisikligi: ${enKotu[0]} ` +
  `(A ${enKotu[2].modul} modul, B ${enKotu[3].modul} modul)`);
```

```
8 modul; bagimlilik A 15, B 9

degisiklik          yuzey   A dosya A modul A der  B dosya B modul B der
ucret tarifesi      evet          6       5     1        2       1     0
gecikme esigi       hayir         3       3     0        1       1     0
uye alani           evet          7       6     1        2       1     0
odunc suresi        hayir         4       3     0        2       1     0
rezervasyon kurali  evet          5       4     1        4       3     1
katalog alani       evet          4       3     0        2       1     0
bildirim metni      hayir         2       1     0        2       1     0
sube takvimi        hayir         3       2     0        2       1     0
rapor sutunu        hayir         3       2     0        2       1     0
para birimi         evet          9       8     2        9       8     1
kimlik alani        evet          9       8     1        2       1     0
indirim kurali      hayir         4       3     0        2       1     0

degisiklik basina ortalama — A: dosya 4.92, modul 4.00, derinlik 0.50
degisiklik basina ortalama — B: dosya 2.67, modul 1.75, derinlik 0.17
iki yapinin da en pahali degisikligi: para birimi (A 8 modul, B 8 modul)
```

Üç ölçüt de aynı yönü gösteriyor ama farklı büyüklükte ayrışıyor. Dosyada oran 1,8; modülde 2,3;
derinlikte 3. En zayıf ayrım dosyada, çünkü doğrudan dokunulan dosya sayısının büyük bölümü işin
kendisinden gelir — bir tarife değişikliği hangi yapıda olursa olsun tarifenin yazıldığı iki dosyayı
ister. **Yapı farkı dosyada değil, dosyaların hangi modüllere dağıldığında görünüyor.**

Farkın tamamı yüzeyi değiştiren altı değişiklikten geliyor. Yüzeyi değiştirmeyen altı istekte
yayılma yok, derinlik sıfır ve dağınık yapıda bile iki ile dört dosya arasında kalınıyor. Yapının
bedeli her değişiklikte değil, yalnız dışa açık yüzeye dokunan değişiklikte ödeniyor; ölçüm bu iki
kümeyi ayırmazsa ortalama, farkı olduğundan küçük gösterir.

Bir satır iki yapıda da aynı: para birimi değişikliği ikisinde de sekiz modüle dokunuyor. Derli
yapıda kavram tek modülde toplanmış, ama o modül ortak modül ve ona herkes bağlı. **Toplamak,
kavram zaten herkesin bağlı olduğu yerdeyse bir şey kazandırmaz.** Modüler yapının kazancı seçici:
kavramı bir kenara toplayabildiği yerde büyük, merkeze toplamak zorunda kaldığı yerde sıfır.

#### Ölçütler Bozulmayı Gösteriyor mu

Bir ölçütün asıl sınavı tek bir ölçümde değil, zaman içinde verdiği eğridedir. Aşağıdaki blok derli
yapının altı çeyrekte bozulmasını **modelliyor** — her çeyrekte bir kavram fazladan bir modüle
sızıyor, iki çeyrekte ayrıca yeni bir bağımlılık kenarı açılıyor — ve üç ölçütün bu bozulmayı
gösterip göstermediğini sınıyor. Sonra bir eşik kuralı yazıp altıncı çeyrekteki gerçek maliyet
kaydında koşturuyor.

```js
// bozulma.mjs — olcutlerin zaman icinde bozulmayi gosterip gostermedigini sinar ve esik
// kuralini bilinen maliyet kaydinda kosturur
import { BAGIMLILIK, DEGISIKLIK, olc } from "./model/model.mjs";

// KN27: B yapisi alti ceyrekte bozuluyor. Her ceyrekte bir kavram fazladan bir module siziyor
// [kavram, modul]; iki ceyrekte ayrica yeni bir bagimlilik kenari aciliyor. Model.
const SIZINTI = [
  ["ucret tarifesi", "sube", null],
  ["uye alani", "odunc", null],
  ["odunc suresi", "sube", ["rapor", "ucret"]],
  ["ucret tarifesi", "rapor", null],
  ["katalog alani", "rapor", ["bildirim", "uyelik"]],
  ["uye alani", "rapor", null],
];

const yerlesim = DEGISIKLIK.map(([ad, yuzey, , b]) => [ad, yuzey, { ...b }]);
const olcumler = [];
const cek = () => {
  const o = yerlesim.map(([ad, yuzey, y]) => [ad, olc("B", yuzey, y)]);
  const ort = (alan) => o.reduce((s, [, v]) => s + v[alan], 0) / o.length;
  return { o, dosya: ort("dosya"), modul: ort("modul"), derinlik: ort("derinlik"),
    ihlal: o.filter(([, v]) => v.modul > 2).length };
};
olcumler.push(cek());
for (const [kavram, modul, kenar] of SIZINTI) {
  const y = yerlesim.find(([ad]) => ad === kavram)[2];
  y[modul] = (y[modul] || 0) + 1;
  if (kenar) BAGIMLILIK.B.push(kenar);
  olcumler.push(cek());
}

console.log(`${"ceyrek".padEnd(8)}${"dosya/degisiklik".padStart(17)}${"modul/degisiklik".padStart(17)}` +
  `${"derinlik".padStart(10)}${"esigi asan degisiklik".padStart(23)}  (Q0 = bozulmadan once)`);
olcumler.forEach((m, i) =>
  console.log(`${`Q${i}`.padEnd(8)}${m.dosya.toFixed(2).padStart(17)}` +
    `${m.modul.toFixed(2).padStart(17)}${m.derinlik.toFixed(2).padStart(10)}` +
    `${String(m.ihlal).padStart(23)}`));

const ilkHareket = (alan) => olcumler.findIndex((m, i) => i > 0 && m[alan] > olcumler[0][alan]);
const artan = (alan) => olcumler.every((m, i) => i === 0 || m[alan] >= olcumler[i - 1][alan]);
for (const alan of ["dosya", "modul", "derinlik", "ihlal"])
  console.log(`${alan.padEnd(9)} ilk hareket Q${ilkHareket(alan)}, ` +
    `bastan sona artis ${(olcumler.at(-1)[alan] - olcumler[0][alan]).toFixed(2)}, ` +
    `hic geri gitmiyor mu: ${artan(alan) ? "evet" : "hayir"}`);

// KN28: alti ceyrek sonunda on iki degisikligin gercek maliyeti ekip kaydindan okunuyor;
// dordu pahali gecmis. Kayit bir gozlemdir, olcutten cikmaz. Model.
const PAHALI = ["ucret tarifesi", "uye alani", "rezervasyon kurali", "katalog alani"];
// Kural: bir degisiklik en fazla iki module dokunmali.
const ESIK = 2;
const son = olcumler.at(-1).o;
let yak = 0, kac = 0, yanlis = 0, sessiz = 0;
const ayrik = [];
for (const [ad, v] of son) {
  const oter = v.modul > ESIK, pahali = PAHALI.includes(ad);
  if (oter && pahali) yak += 1; else if (oter) yanlis += 1;
  else if (pahali) kac += 1; else sessiz += 1;
  if (oter !== pahali) ayrik.push(`  ${oter ? "yanlis alarm" : "kacan"}: ${ad} ` +
    `(${v.modul} modul, kayitta ${pahali ? "pahali" : "ucuz"})`);
}
console.log(`\nesik ${ESIK} modul, ${son.length} degisiklik, ${PAHALI.length} pahali: ` +
  `yakalanan ${yak}, kacan ${kac}, yanlis alarm ${yanlis}, dogru sessizlik ${sessiz}`);
for (const s of ayrik) console.log(s);

// KN29: cevrilemeyen kisim. Olcut genisligi sayar, zorlugu sayamaz; bir kavramin dogru
// modulde olup olmadigi da bir yargidir. Yerine ceyrekte bir yerlesim gozden gecirmesi
// konur: 12 degisiklik x 1 kisi-saat.
const SAAT = 1;
console.log(`\ncevrilemeyen: degisikligin zorlugu ve kavramin dogru modulde olmasi (yargi); ` +
  `yanlis alarm ${yanlis}, kacan ${kac} tam olarak bu kapsamda`);
console.log(`yerine konan: ceyrekte bir yerlesim gozden gecirmesi, ` +
  `${son.length * SAAT} kisi-saat/ceyrek, alti ceyrekte ${son.length * SAAT * 6}`);
const kayit = DEGISIKLIK.length * 3 + BAGIMLILIK.B.length;
console.log(`olcut maliyeti: ${kayit} kayit (12 degisiklik x 3 alan + ` +
  `${BAGIMLILIK.B.length} bagimlilik kenari); kosum ${olcumler.length * son.length} olcum; ` +
  `degisim maliyeti, borc faizi olarak olculen seyin gozlenen yuzudur`);
```

```
ceyrek   dosya/degisiklik modul/degisiklik  derinlik  esigi asan degisiklik  (Q0 = bozulmadan once)
Q0                   2.67             1.75      0.17                      2
Q1                   2.75             1.83      0.17                      2
Q2                   3.00             2.08      0.25                      3
Q3                   3.17             2.25      0.33                      4
Q4                   3.17             2.25      0.25                      4
Q5                   3.42             2.50      0.33                      4
Q6                   3.42             2.50      0.33                      4
dosya     ilk hareket Q1, bastan sona artis 0.75, hic geri gitmiyor mu: evet
modul     ilk hareket Q1, bastan sona artis 0.75, hic geri gitmiyor mu: evet
derinlik  ilk hareket Q2, bastan sona artis 0.17, hic geri gitmiyor mu: hayir
ihlal     ilk hareket Q2, bastan sona artis 2.00, hic geri gitmiyor mu: evet

esik 2 modul, 12 degisiklik, 4 pahali: yakalanan 3, kacan 1, yanlis alarm 1, dogru sessizlik 7
  kacan: katalog alani (2 modul, kayitta pahali)
  yanlis alarm: para birimi (8 modul, kayitta ucuz)

cevrilemeyen: degisikligin zorlugu ve kavramin dogru modulde olmasi (yargi); yanlis alarm 1, kacan 1 tam olarak bu kapsamda
yerine konan: ceyrekte bir yerlesim gozden gecirmesi, 12 kisi-saat/ceyrek, alti ceyrekte 72
olcut maliyeti: 47 kayit (12 degisiklik x 3 alan + 11 bagimlilik kenari); kosum 84 olcum; degisim maliyeti, borc faizi olarak olculen seyin gozlenen yuzudur
```

Dosya ve modül ölçütleri ilk çeyrekte hareket ediyor ve altı çeyrek boyunca hiç geri gitmiyor.
Derinlik ölçütü ikisini de yapmıyor: ikinci çeyrekte hareket ediyor, dördüncü çeyrekte geri gidiyor.
Yapı yalnız kötüleşirken ölçüt bir çeyrek iyileşmiş görünüyor. Nedeni ölçütün tanımında: sızıntı,
bir kavramı daha önce dolaylı dokunulan modüle doğrudan yazıyor, o modül artık yayılma değil
başlangıç sayılıyor ve derinlik düşüyor. **Bozulmanın kendisi ölçütün girdisini değiştiriyorsa
ölçüt tek yönlü olmaz** — ve bu, ölçütü kullanışsız kılmaz, yalnız tek başına okunamaz kılar.

Eşiği aşan değişiklik sayısı basamak basamak ilerliyor: iki, üç, dört, sonra üç çeyrek sabit. Kaba
sayım geç hareket ediyor ve son üç çeyrekteki bozulmayı hiç göstermiyor; ortalama ise aynı üç
çeyrekte 2,25'ten 2,50'ye çıkıyor. İkisi birbirinin yerine geçmiyor: ortalama erken uyarı verir,
eşik sayımı karar noktası olur.

Kuralın kendisi altıncı çeyrekte on iki değişikliğin dördü pahalı geçmişken üç yakalama, bir kaçan,
bir yanlış alarm ve yedi doğru sessizlik üretiyor. Kaçan, iki modüle dokunduğu halde pahalı geçen
katalog alanı değişikliği; yanlış alarm, sekiz modüle dokunduğu halde ucuz geçen para birimi
değişikliği — sekiz yerde aynı sabitin adı düzeltilmiş. **İki hata da tek kaynaktan geliyor: ölçüt
genişliği sayıyor, zorluğu değil.** Çevrilemeyen kısım tam olarak budur; değişikliğin zorluğu ve
kavramın doğru modülde olup olmadığı bir yargıdır. Yerine çeyrekte bir yerleşim gözden geçirmesi
konuyor: on iki değişiklik için 12 kişi-saat, altı çeyrekte 72.

Ölçütün kendi maliyeti küçük görünüyor — 47 kayıt ve 84 ölçüm. Ama kayıtların hepsi elle tutuluyor:
her değişiklik isteğinin hangi kavrama dokunduğu, yüzeyi değiştirip değiştirmediği ve hangi modülde
kaç dosyada geçtiği. Bu kayıt bırakılırsa ölçüt susmaz, yanlış konuşur; bozulmayı gösteren eğri,
kaydın eskime eğrisi olur.

#### Özet

- Aynı on iki değişiklik iki yapıda koşturulduğunda dağınık yapı değişiklik başına 4,92 dosya, 4,00
  modül ve 0,50 derinlik; derli yapı 2,67 dosya, 1,75 modül ve 0,17 derinlik istiyor.
- Ayrım en zayıf dosyada (1,8 kat), en keskin derinlikte (3 kat); farkın tamamı yüzeyi değiştiren
  altı değişiklikten geliyor, ötekilerde iki yapı ayrışmıyor.
- Ortak modüle dokunan değişiklik iki yapıda da sekiz modüle yayılıyor: toplamak, kavram zaten
  herkesin bağlı olduğu yerdeyse kazandırmıyor.
- Altı çeyreklik bozulmada dosya ve modül ölçütleri ilk çeyrekte hareket edip hiç geri gitmiyor;
  derinlik dördüncü çeyrekte geri gidiyor, çünkü sızıntı dolaylı dokunuşu doğrudana çeviriyor.
- Eşik kuralı 3 yakalanan, 1 kaçan, 1 yanlış alarm, 7 doğru sessizlik veriyor; iki hata da ölçütün
  genişliği sayıp zorluğu saymamasından geliyor.
- Çevrilemeyen kısmın yerine çeyrekte 12 kişi-saatlik yerleşim gözden geçirmesi konuyor; ölçütün
  koşumu 84 ölçüm, bakımı 47 elle tutulan kayıt.

#### Sonraki Adım

Bu konu nitelikleri ölçüye bağladı. Ağaçta otuz yaprak sayıldı ve hangisinin denetime çevrilebildiği
ayrıldı; gerilim matrisi hangi kararın hangi niteliği bastırdığını gösterdi; güven sınırları ve
denetimsiz geçişler sayıldı; ölçek biriminin üç adayı kopyalanan, paylaşılan ve kopyalanamayan
üzerinden karşılaştırıldı; değişim maliyeti iki yapıda doğrudan ölçüldü. Artık hangi kararın hangi
niteliği koruduğu yazılabiliyor.

Ama bu ölçülerin hepsi **bir kez** alındı. Eşikler bir günün eşiği, grafik bir günün grafiği,
yerleşim bir günün yerleşimi. Altı ay sonra hâlâ geçerli olup olmadığını söyleyen bir şey yok — bu
dersin bozulma tablosu bile elle kurulmuş bir modeldi, kendiliğinden koşan bir düzen değil.
Ölçümü tekrarlayan, sapmayı bildiren ve her ölçünün kime ait olduğunu belirleyen bir mekanizma
gerekiyor; onsuz bu konudaki bütün sayılar, alındıkları günün fotoğrafı olarak kalır.

## Yönetişim

Kararın sürdüğünün denetlenmesi: merkezî, federatif ve danışman yönetişim modellerinin karar hızı ile tutarlılık arasındaki takası, mimari kuralları otomatik sınayan uygunluk işlevleri, bağımlılık ve katman kurallarının derlemede denetlenmesi, değişime dayanıklı evrimsel mimari kurma ve düzenleyici kısıtların mimariye gömülmesi.

### Mimari Yönetişim Modelleri

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/mimari-yonetisim-modelleri

Merkezî, federatif ve danışman yönetişimin aynı karar akışında koşturulması: karar başına bekleme turu, kurala uyum oranı, yakalanan ve kaçan ihlal, yerel bağlamın kaçırılmasından doğan yanlış alarm ve karar hızı ile tutarlılık arasındaki takasın taranması.

Önceki konu her niteliği bir ölçüye bağladı ve hangi kararın hangi niteliği koruduğunu yazdı. O
ölçülerin ortak bir kusuru var: hepsi **bir kez** alındı. Bir ölçü alındığı gün doğrudur; altı ay
sonra hâlâ doğru olup olmadığını söyleyen bir mekanizma yoktur. Bu arada yeni modüller girer, yeni
bağımlılıklar açılır ve kararı verenler ortamdan ayrılır. Ölçünün tazeliğini koruyan şey ölçünün
kendisi değil, kararların hangi düzenle alındığıdır.

Yönetişim tam olarak budur: mimari kararların kim tarafından, hangi sırayla ve hangi kurala göre
alındığı. Bu ilk ders karar akışının kendisini ölçer; sonraki iki ders kuralların makine tarafından
denetlenmesini yazar. Through-line **bölgesel kütüphane ağıdır** — şube sistemleri, dışarıdan
alınmış katalog, içeride yazılmış ödünç ve ücretlendirme servisleri, ayrı bir üyelik sistemi ve
belediyedeki kimlik servisi — ve kurgudur.

#### Ölçülecek Karar Akışı

Aşağıdaki blok bir **modeldir**, ölçüm değil: gerçek bir kurumun karar kaydından çıkarılmamıştır,
karar akışını üç yönetişim modelinde koşturulabilir hâle getiren bir veri yapısıdır. Modelin
girdisi bir çeyreğin mimari kararlarıdır.

**YN1 — bir çeyrekte yirmi dört mimari karar alınır ve her kararın gerçekten kural ihlali olup
olmadığı sonradan bilinmektedir.** Gerekçe: yakalama ve kaçırma ancak bilinen bir ihlal kümesine
karşı sayılabilir; bu ders o kümeyi girdi olarak sabitler. Her kararda dört ayrı bilgi taşınır:
**belirti** (kararın yüzeyde kurala takılan yanı), **yerel mazeret** (kararı meşru kılan ama yalnız
birimin gördüğü ayrıntı), **örtük belirti** (ancak ortak bir gözden geçirmede fark edilen yan) ve
kararın **kural sürümüne** duyarlı olup olmadığı.

```js
// yonetisim.mjs — ayni karar akisini uc yonetisim modelinde kosturur (model)
// bayrak: b belirti  m yerel mazeret  y yeni kural  g kural gevsedi  o ortuk belirti  d danisildi
import { writeFileSync } from "node:fs";

const KARAR = [
  "katalog tablosuna dogrudan yazma      | odunc   | ortak | bo",
  "sube ici raf onbellegi                | sube    | yerel | bm",
  "uye alanlarinin kopyalanmasi          | ucret   | ortak | b",
  "kimlik servisine sube ucundan cagri   | sube    | ortak | bmd",
  "raf etiketi alani                     | katalog | yerel | -",
  "ortak ucta surum etiketinin dusmesi   | katalog | ortak | by",
  "gecikme hesabinin odunce tasinmasi    | ucret   | ortak | b",
  "adres alaninin bildirimde saklanmasi  | uyelik  | yerel | bmd",
  "dis katalog yanitinin ham saklanmasi  | odunc   | ortak | byd",
  "sube raporunun depodan okunmasi       | sube    | ortak | b",
  "ucret tablosuna odunc tarafindan yazma| odunc   | ortak | bo",
  "dondurma bayraginin oduncte tutulmasi | uyelik  | ortak | bm",
  "kimlik belirtecinin gunluge yazilmasi | kimlik  | ortak | by",
  "bildirim metninin uyelikte tutulmasi  | uyelik  | yerel | bg",
  "zaman asimi degerinin elle ayari      | odunc   | yerel | -",
  "sube ici toplu ice aktarma            | sube    | yerel | bmd",
  "dis katalogdan fiyat okuma            | ucret   | ortak | bo",
  "katalog ucunun sayfalamasiz yayimi    | katalog | ortak | byd",
  "uyelik kaydinin subede cogaltilmasi   | uyelik  | ortak | bm",
  "kimlik onbellek suresinin uzatilmasi  | kimlik  | yerel | bgd",
  "odunc olayinin bildirime yazilmasi    | odunc   | ortak | bd",
  "sube ekraninda dogrudan sorgu         | sube    | yerel | bo",
  "ucret iadesinin uyelige dokunmasi     | ucret   | ortak | bm",
  "arama sonucunun subede onbelleklenmesi| katalog | yerel | bmd",
].map((s) => {
  const [ad, birim, kapsam, bayrak] = s.split("|").map((x) => x.trim());
  const v = (c) => bayrak.includes(c);
  return { ad, birim, kapsam, belirti: v("b"), mazeret: v("m"), yeni: v("y"),
           gevsek: v("g"), ortuk: v("o"), danisildi: v("d") };
});

// YN2: kural surumu 2 yururlukte; "yeni" kararlar 2 ile ihlal, 1 ile uygun okunur; "gevsek"
// kararlar tersi. Birimlerin surumu modele gore dagilir (esgudum ritueli kadar guncel kalirlar).
const SURUM = {
  federatif: { sube: 2, katalog: 1, odunc: 2, ucret: 2, uyelik: 2, kimlik: 1 },
  danisman:  { sube: 1, katalog: 1, odunc: 2, ucret: 1, uyelik: 1, kimlik: 1 },
};

const gercek = (k) => (k.gevsek ? false : k.belirti && !k.mazeret);   // bilinen ihlal kumesi
const birimKarari = (k, s) => {
  if (k.ortuk) return false;                       // birim tek basina ortuk belirtiyi gormez
  if (k.yeni) return s === 2 && k.belirti && !k.mazeret;
  if (k.gevsek) return s === 1 && k.belirti;
  return k.belirti && !k.mazeret;
};
const MODEL = {
  "merkezi":            (k) => k.belirti && !k.gevsek,          // ceker gorur, mazereti gormez
  "federatif":          (k) => k.yeni || k.gevsek
    ? birimKarari({ ...k, ortuk: false }, SURUM.federatif[k.birim])
    : k.belirti && !k.mazeret,                                  // ortak liste ortugu acar
  "danisman":           (k) => k.danisildi ? gercek(k) : birimKarari(k, SURUM.danisman[k.birim]),
  "merkezi + soru turu": (k) => gercek(k),                       // yerel baglami sorar
};

// bekleme: her model kendi kuyrugunu kendi kapasitesiyle isler (tur basina karar)
function kuyruk(liste, kapasite, ekTur = () => 0) {
  const sira = liste.map((k) => ({ k, kalan: 1 + ekTur(k) }));
  const tur = new Map(liste.map((k) => [k.ad, 0]));
  let t = 0;
  while (sira.length) {
    t += 1; let kap = kapasite; const ertelenen = [];
    while (kap > 0 && sira.length) {
      const it = sira.shift(); kap -= 1; it.kalan -= 1;
      if (it.kalan === 0) tur.set(it.k.ad, t); else ertelenen.push(it);
    }
    sira.push(...ertelenen);
  }
  return tur;
}
const BEKLEME = {
  "merkezi": kuyruk(KARAR, 3),
  "federatif": new Map([...kuyruk(KARAR.filter((k) => k.kapsam === "ortak"), 4),
                        ...KARAR.filter((k) => k.kapsam === "yerel").map((k) => [k.ad, 0])]),
  "danisman": new Map([...kuyruk(KARAR.filter((k) => k.danisildi), 5),
                       ...KARAR.filter((k) => !k.danisildi).map((k) => [k.ad, 0])]),
  "merkezi + soru turu": kuyruk(KARAR, 3, (k) => (k.mazeret ? 1 : 0)),
};

writeFileSync("karar.json", JSON.stringify(KARAR));
const ihlal = KARAR.filter(gercek).length;
console.log(`karar: ${KARAR.length} (${KARAR.filter((k) => k.kapsam === "ortak").length} ortak kapsamli), ` +
  `bilinen ihlal: ${ihlal}, yerel mazeretli: ${KARAR.filter((k) => k.mazeret).length}, ` +
  `ortuk belirtili: ${KARAR.filter((k) => k.ortuk).length}`);
console.log(`\n${"model".padEnd(21)}${"ort. bekleme".padStart(13)}${"en uzun".padStart(9)}` +
  `${"yakalanan".padStart(11)}${"kacan".padStart(7)}${"yanlis alarm".padStart(14)}${"uyum".padStart(8)}`);
for (const [ad, karar] of Object.entries(MODEL)) {
  const y = KARAR.filter((k) => gercek(k) && karar(k)).length;
  const kc = KARAR.filter((k) => gercek(k) && !karar(k)).length;
  const ya = KARAR.filter((k) => !gercek(k) && karar(k)).length;
  const bek = KARAR.map((k) => BEKLEME[ad].get(k.ad));
  const ort = bek.reduce((a, b) => a + b, 0) / bek.length;
  console.log(`${ad.padEnd(21)}${ort.toFixed(2).padStart(13)}${String(Math.max(...bek)).padStart(9)}` +
    `${`${y}/${ihlal}`.padStart(11)}${String(kc).padStart(7)}${String(ya).padStart(14)}` +
    `${`%${((100 * (KARAR.length - kc - ya)) / KARAR.length).toFixed(1)}`.padStart(8)}`);
}

// yanlis alarmin kaynagi: yerel baglam mi, kural surumu mu
console.log(`\nyanlis alarmin kaynagi (karar adiyla):`);
for (const [ad, karar] of Object.entries(MODEL)) {
  const ya = KARAR.filter((k) => !gercek(k) && karar(k));
  const baglam = ya.filter((k) => k.mazeret).length;
  console.log(`  ${ad.padEnd(21)}${String(ya.length).padStart(3)} yanlis alarm; ` +
    `${baglam} yerel baglamin kacirilmasi, ${ya.length - baglam} eski kural surumu` +
    (ya.length ? ` (${ya.map((k) => k.ad.split(" ")[0]).join(", ")})` : ""));
}
```

```
karar: 24 (15 ortak kapsamli), bilinen ihlal: 12, yerel mazeretli: 8, ortuk belirtili: 4

model                 ort. bekleme  en uzun  yakalanan  kacan  yanlis alarm    uyum
merkezi                       4.50        8      12/12      0             8   %66.7
federatif                     1.50        4       9/12      3             1   %83.3
danisman                      0.46        2       6/12      6             1   %70.8
merkezi + soru turu           6.17       11      12/12      0             0  %100.0

yanlis alarmin kaynagi (karar adiyla):
  merkezi                8 yanlis alarm; 8 yerel baglamin kacirilmasi, 0 eski kural surumu (sube, kimlik, adres, dondurma, sube, uyelik, ucret, arama)
  federatif              1 yanlis alarm; 0 yerel baglamin kacirilmasi, 1 eski kural surumu (kimlik)
  danisman               1 yanlis alarm; 0 yerel baglamin kacirilmasi, 1 eski kural surumu (bildirim)
  merkezi + soru turu    0 yanlis alarm; 0 yerel baglamin kacirilmasi, 0 eski kural surumu
```

#### Üç Modelin Okunması

Modellerin ayrımı kimin neyi gördüğüdür. **Merkezî yönetişim** kararların hepsini tek kurulda
inceler: tek kural sürümü vardır, örtük belirtiyi görür, ama şubenin o önbelleği neden koyduğunu
bilmez. **Federatif yönetişim** kararı birime bırakır, birim yerel mazereti görür ve ortak
kapsamlı kararlar temsilcilerin kuruluna gider; ortak gözden geçirme listesi örtük belirtiyi de
açar. **Danışman yönetişim** kararı tümüyle birime bırakır ve merkez yalnız danışıldığında konuşur.

Merkezî model **on iki ihlalin on ikisini de yakalar ve hiçbirini kaçırmaz**, ama sekiz yanlış
alarm üretir ve uyum oranı %66,7'de kalır. Yanlış alarmların **sekizinin sekizi de yerel bağlamın
kaçırılmasından** doğar: kurul kararın yüzeyini görüp mazereti göremediği için meşru bir kararı
engeller. Karar başına ortalama bekleme 4,5 tur, en uzun bekleme 8 turdur.

Danışman model tersini yapar: ortalama bekleme 0,46 tura iner, ama on iki ihlalin **altısı kaçar**.
Kaçanların dördü örtük belirtili kararlardır — hiçbir gözden geçirme adımı olmadığı için kimse fark
etmez. İkisi eski kural sürümüyle alınmıştır.

Federatif model üçünün en yüksek uyumunu verir (%83,3) ve bunu 1,5 turluk beklemeyle yapar. Bedeli
üç kaçan ihlaldir ve üçü de aynı kaynaktan gelir: kural sürümü güncel olmayan birimler. Tek yanlış
alarmının kaynağı da odur — gevşetilmiş bir kuralı eski sürümle okuyan birim, artık serbest olan
bir kararı engeller. **Federatif modelde ölçülen şey kuralın kendisi değil, yayılma hızıdır.**

Dördüncü satır bir model değil, merkezî modelin bir alt sınırıdır: kurul yerel mazereti olan sekiz
kararı **önceden bildiği** varsayılarak yalnız onlarda soru turu açar. Sonuç eksiksizdir — 12/12
yakalama, sıfır kaçırma, sıfır yanlış alarm, %100 uyum — ve bedeli ortalama beklemenin 4,5'ten 6,17
tura, en uzun beklemenin 8'den 11 tura çıkmasıdır. Bu satır ulaşılabilir bir çalışma biçimi değil,
soru turunun en ucuz hâlidir.

#### Hız ile Tutarlılık Arasındaki Takas

Gerçek bir kurul hangi kararın mazereti olduğunu önceden bilmez; sorusunu sıradaki adaya sorar ve
çoğu soru boşa gider. Aşağıdaki blok bunu tarar: kurul, yüzeyden engelleyeceği yirmi kararın ilk
`s` tanesine soru turu açar.

```js
// takas.mjs — yonetisim.mjs'in yazdigi karar akisini okur; merkezi kurulun soru sayisi taranir
import { readFileSync } from "node:fs";

const KARAR = JSON.parse(readFileSync("karar.json", "utf8"));
const gercek = (k) => (k.gevsek ? false : k.belirti && !k.mazeret);
const yuzeyde = (k) => k.belirti && !k.gevsek;                 // kurulun soru sormadan gordugu

function bekleme(sorulan) {                                    // sorulan karar bir tur daha bekler
  const sira = KARAR.map((k) => ({ k, kalan: sorulan.has(k.ad) ? 2 : 1 }));
  const tur = new Map(); let t = 0;
  while (sira.length) {
    t += 1; let kap = 3; const ertelenen = [];
    while (kap > 0 && sira.length) {
      const it = sira.shift(); kap -= 1; it.kalan -= 1;
      if (it.kalan === 0) tur.set(it.k.ad, t); else ertelenen.push(it);
    }
    sira.push(...ertelenen);
  }
  return [...tur.values()];
}

const engellenecek = KARAR.filter(yuzeyde);                    // kurulun bakacagi 20 karar
console.log(`kurulun yuzeyden engelleyecegi karar: ${engellenecek.length}; ` +
  `bunlarin ${engellenecek.filter((k) => k.mazeret).length} tanesinin yerel bir mazereti var`);
console.log(`\n${"sorulan".padStart(8)}${"ort. bekleme".padStart(13)}${"ek bekleme".padStart(12)}` +
  `${"en uzun".padStart(9)}${"yanlis alarm".padStart(14)}${"kalkan alarm".padStart(14)}` +
  `${"alarm / ek tur".padStart(16)}`);
for (const s of [0, 4, 8, 12, 16, 20]) {
  const sorulan = new Set(engellenecek.slice(0, s).map((k) => k.ad));
  const karar = (k) => (sorulan.has(k.ad) ? gercek(k) : yuzeyde(k));
  const ya = KARAR.filter((k) => !gercek(k) && karar(k)).length;
  const b = bekleme(sorulan);
  const ort = b.reduce((a, c) => a + c, 0) / b.length;
  const ek = ort - 4.5;
  console.log(`${String(s).padStart(8)}${ort.toFixed(2).padStart(13)}${ek.toFixed(2).padStart(12)}` +
    `${String(Math.max(...b)).padStart(9)}${String(ya).padStart(14)}${String(8 - ya).padStart(14)}` +
    `${(s === 0 ? "-" : ((8 - ya) / ek).toFixed(2)).padStart(16)}`);
}
```

```
kurulun yuzeyden engelleyecegi karar: 20; bunlarin 8 tanesinin yerel bir mazereti var

 sorulan ort. bekleme  ek bekleme  en uzun  yanlis alarm  kalkan alarm  alarm / ek tur
       0         4.50        0.00        8             8             0               -
       4         5.83        1.33       10             6             2            1.50
       8         7.13        2.63       11             5             3            1.14
      12         8.38        3.88       12             4             4            1.03
      16         9.54        5.04       14             2             6            1.19
      20        10.67        6.17       15             0             8            1.30
```

İlk dört soru 1,33 turluk ek beklemeyle iki yanlış alarm kaldırır; bütün adaylara sormak sekizini
de kaldırır ve karar başına ortalama beklemeyi 4,5'ten 10,67 tura çıkarır. Asıl ölçü son sütundur:
**ek bekleme turu başına kaldırılan yanlış alarm**. Kurul hangi adaya soracağını önceden bilemediği
için soruların çoğu boşa gider — yirmi sorunun yalnız sekizi bir alarmı kaldırır.

**Hiçbir model her iki uçta birden iyi değildir**: merkezî model kaçırmayı sıfırlar, danışman model
beklemeyi sıfırlar, federatif model ikisinin arasında en yüksek uyumu verir ve bedelini kural
yayılımında öder.

#### Yönetişim Kuralı Denetime Çevrilebilir mi

Yukarıdaki modelin tamamı bir varsayıma dayanır: kararı inceleyen **insandır**. Bu kursun sorusu
ise kuralın makinece denetlenip denetlenemeyeceğidir. Kural kümesi bu gözle ayrıldığında ortaya iki
yığın çıkar.

```js
// kural.mjs — yonetisim kural kumesinin denetlenebilirligi ve orneklemin yakaladigi
import { readFileSync } from "node:fs";

const KURAL = [                                   // her satir: kural | makinece denetlenebilir mi
  ["her ortak uc surum etiketi tasir", true],
  ["hicbir birim baska birimin tablosuna yazmaz", true],
  ["her dis cagri zaman asimi degeri tasir", true],
  ["kimlik belirteci gunluge yazilmaz", true],
  ["yeni bagimlilik bir karar kaydiyla gelir", true],
  ["sinir, degisim birlikteligine gore cizilir", false],
  ["karar en az iki secenegin bedeliyle gerekcelendirilir", false],
  ["yerel cozum ortak sozlesmeye donusmeden once tartisilir", false],
  ["disa acilan ad, modulun isini anlatir", false],
];
const cevrilen = KURAL.filter(([, d]) => d);
console.log(`kural: ${KURAL.length}; denetime cevrilen ${cevrilen.length}, cevrilemeyen ${KURAL.length - cevrilen.length}`);

// YN3: cevrilemeyen kurallar icin yerine konan sey uc kararda bir orneklem incelemesidir
const KARAR = JSON.parse(readFileSync("karar.json", "utf8"));
const gercek = (k) => (k.gevsek ? false : k.belirti && !k.mazeret);
const ornek = KARAR.filter((_, i) => i % 3 === 2);
const yakalanan = ornek.filter(gercek).length;
const toplam = KARAR.filter(gercek).length;
console.log(`\norneklem: ${KARAR.length} kararin ${ornek.length} tanesi incelendi (1/3); ` +
  `${toplam} ihlalin ${yakalanan} tanesi orneklemin icinde, ${toplam - yakalanan} tanesi disinda`);
console.log(`inceleme yuku: ${ornek.length} karar x ${KURAL.length - cevrilen.length} cevrilemeyen kural = ` +
  `${ornek.length * (KURAL.length - cevrilen.length)} elle bakis`);
```

```
kural: 9; denetime cevrilen 5, cevrilemeyen 4

orneklem: 24 kararin 8 tanesi incelendi (1/3); 12 ihlalin 5 tanesi orneklemin icinde, 7 tanesi disinda
inceleme yuku: 8 karar x 4 cevrilemeyen kural = 32 elle bakis
```

Dokuz kuralın beşi bir denetime çevrilir: her biri kaynakta ya da yapılandırmada aranabilir bir
örüntüye karşılık gelir. Dördü çevrilemez, çünkü ölçtükleri şey metnin biçimi değil gerekçenin
niteliğidir; "iki seçeneğin bedeliyle gerekçelendirme" kuralını sınayan bir işlev, gerekçenin var
olup olmadığını sayabilir ama yeterli olup olmadığını sayamaz.

Çevrilemeyen dördünün yerine konan şey **örneklem incelemesidir** ve bedeli iki yönlüdür: sekiz
karara dört kural için otuz iki elle bakış ve kapsam eksikliği — on iki bilinen ihlalin **yedisi
örneklemin dışında kalır**. Denetime çevrilen beş kuralda bu eksiklik yoktur; onlar her karara
uygulanır. Makinece denetlenen kural arttıkça insanın gözden geçireceği kural azalır ve kurulun
bekleme turu düşer.

#### Özet

- Aynı 24 kararlık akış üç modelde koşturuldu; merkezî model 12 ihlalin 12'sini yakalar ve hiç
  kaçırmaz, ama 8 yanlış alarm verir ve uyum %66,7'de kalır — alarmların 8'i de yerel bağlamın
  kaçırılmasından doğar.
- Danışman model beklemeyi 0,46 tura indirir ve 12 ihlalin 6'sını kaçırır; kaçanların 4'ü hiçbir
  gözden geçirmede görülmeyen örtük belirtili kararlardır.
- Federatif model en yüksek uyumu verir (%83,3, 1,50 tur bekleme) ve tek zayıflığı kural
  yayılımıdır: 3 kaçan ihlalin ve 1 yanlış alarmın kaynağı güncel olmayan kural sürümüdür.
- Merkezî kurul yerel bağlamı sorarak %100 uyuma çıkar; bedeli ortalama beklemenin 4,50'den 10,67
  tura, en uzun beklemenin 8'den 15 tura çıkmasıdır ve 20 sorunun yalnız 8'i bir alarmı kaldırır.
- Dokuz yönetişim kuralının 5'i denetime çevrilir, 4'ü çevrilemez; çevrilemeyenlerin yerine konan
  1/3 örneklem 32 elle bakış ister ve 12 ihlalin 7'sini görmez.

#### Sonraki Adım

Bu dersin son tablosu bir sınır çizdi: dokuz kuralın beşi makinece denetlenebilir. O beşi denetime
çevirmenin bedeli henüz sayılmadı. Bir mimari kuralı koşturulabilir bir işleve çevirmek, kuralı bir
**sayıya** ve bir **eşiğe** indirmek demektir; eşik seçildiği anda hem kaçan ihlal hem de yanlış
alarm ortaya çıkar. Sonraki ders bir uygunluk işlevini gerçekten yazar: bir mimari kuralı sayıya
çeviren işlev bilinen bir ihlal kümesinde koşturulur, eşiği baştan sona taranır ve her eşikte kaç
ihlalin yakalandığı, kaçının kaçtığı, kaç yanlış alarm üretildiği sayılır. Aynı koşumda ikinci bir
soru daha sorulur — yanlış alarm veren bir kuralın ekipte ürettiği davranış nedir ve kural devre
dışı bırakıldığında arkasından kaç ihlal geçer.

### Uygunluk İşlevleri

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/uygunluk-islevleri

Bir mimari kuralın koşturulabilir bir sayıya çevrilmesi: iki uygunluk işlevinin gerçek modül dosyaları üzerinde yazılması, bilinen sızıntı kümesinde yakalanan ve kaçan ihlal ile yanlış alarmın sayılması, eşiğin baştan sona taranması, kuralın satır ve dosya okuma maliyeti ve yanlış alarmın kuralı devre dışı bıraktırması.

Önceki ders dokuz yönetişim kuralının beşinin bir denetime çevrilebileceğini yazdı ama çevirinin
bedelini saymadı. Çeviri şu demektir: kural bir **sayıya** ve bir **eşiğe** indirilir. Sayıyı üreten
işleve **uygunluk işlevi** denir; kaynağı, yapılandırmayı ya da bir grafiği okur ve tek bir nicelik
döndürür. Eşik ise kararın kendisidir — bu sayının üstü ihlal, altı uygundur.

Eşik seçildiği anda iki hata birden doğar. Eşik gevşekse gerçek ihlaller altından geçer; sıkıysa
kurallara uygun modüller ihlal diye işaretlenir. Bu ikili, kalite göstergelerindeki **kaçan hata**
ile **yanlış geçme** olgusunun aynısıdır; buradaki fark, hatayı yapanın bir sınama değil bir mimari
kural olmasıdır. Bu ders bir uygunluk işlevini gerçekten yazar, bilinen bir sızıntı kümesinde
koşturur ve üç sayıyı birden sayar: yakalanan, kaçan, yanlış alarm. Through-line **bölgesel
kütüphane ağıdır** ve kurgudur.

#### Ölçülecek Modül Kümesi

Denetlenecek kural şudur: **bir modül, dışarıya işini yapmak için gerekenden fazlasını açmaz.**
Cümle hâliyle denetlenemez; denetlenebilmesi için "fazlasının" bir sayıya bağlanması gerekir.
Aşağıdaki blok ölçülecek kod tabanını diske yazar: on iki modül, her biri kendi dışa açık
sembollerini dışa aktarır ve başkalarının sembollerini ithal eder.

**YN4 — bölgesel kütüphane ağının kod tabanı on iki modülden oluşur ve her modülün dışa açtığı
sembol sayısı ile bu sembolleri kimin ithal ettiği aşağıdaki haritadan üretilir.** Gerekçe: uygunluk
işlevi gerçek dosyaları okumalıdır; harita, okunacak dosyaları üreten girdidir. Sayılar değişirse
aşağıdaki eşik taraması da değişir, işlevin kendisi değişmez.

```js
// kur.mjs — bolgesel kutuphane agi modullerini disa acilan sembolleriyle diske yazar
import { mkdirSync, writeFileSync } from "node:fs";

// acik: disa acilan sembol sayisi | tuketici: hangi modul hangi sembol sirasini ithal ediyor
const MODUL = {
  ortakBicim:      { acik: 9, tuketici: { raporlama: [0, 1, 2], oduncAkisi: [0, 3, 4],
                                          subeUcu: [1, 5], ayarlar: [2] } },
  subeUcu:         { acik: 11, tuketici: { raporlama: [0, 1, 2] } },
  katalogKoprusu:  { acik: 7, tuketici: { oduncAkisi: [0, 1, 2, 3, 4, 5, 6] } },
  oduncAkisi:      { acik: 8, tuketici: { subeUcu: [0, 1, 2, 3], raporlama: [4, 5],
                                          ucretHesabi: [6] } },
  ucretHesabi:     { acik: 4, tuketici: { subeUcu: [0] } },
  uyelikKaydi:     { acik: 3, tuketici: { subeUcu: [0, 1], oduncAkisi: [2] } },
  kimlikKoprusu:   { acik: 2, tuketici: { subeUcu: [0], uyelikKaydi: [1] } },
  bildirimKuyrugu: { acik: 5, tuketici: { oduncAkisi: [0] } },
  raporlama:       { acik: 6, tuketici: { subeUcu: [0, 1, 2, 3, 4, 5] } },
  depoErisimi:     { acik: 3, tuketici: { oduncAkisi: [0] } },
  olayGunlugu:     { acik: 4, tuketici: { oduncAkisi: [0] } },
  ayarlar:         { acik: 4, tuketici: { subeUcu: [0], oduncAkisi: [1], raporlama: [2],
                                          ucretHesabi: [3] } },
};

mkdirSync("modul", { recursive: true });
for (const ad of Object.keys(MODUL)) {
  const ithal = Object.entries(MODUL)
    .filter(([, m]) => m.tuketici[ad])
    .map(([hedef, m]) => `import { ${m.tuketici[ad].map((i) => `${hedef}${i + 1}`).join(", ")} }` +
      ` from "./${hedef}.mjs";`);
  const disa = Array.from({ length: MODUL[ad].acik },
    (_, i) => `export const ${ad}${i + 1} = (x) => x;`);
  writeFileSync(`modul/${ad}.mjs`, `${ithal.join("\n")}\n${disa.join("\n")}\n`);
}

const kenar = Object.values(MODUL).reduce((s, m) => s + Object.keys(m.tuketici).length, 0);
const sembol = Object.values(MODUL).reduce((s, m) => s + m.acik, 0);
console.log(`${Object.keys(MODUL).length} modul dosyasi yazildi; ` +
  `${sembol} disa acilan sembol, ${kenar} ithal kenari`);
console.log(`\n${"modul".padEnd(17)}${"disa acilan".padStart(12)}   ithal eden modul`);
for (const [ad, m] of Object.entries(MODUL)) {
  console.log(`${ad.padEnd(17)}${String(m.acik).padStart(12)}   ` +
    `${Object.keys(m.tuketici).join(", ") || "-"}`);
}
```

```
12 modul dosyasi yazildi; 66 disa acilan sembol, 22 ithal kenari

modul             disa acilan   ithal eden modul
ortakBicim                  9   raporlama, oduncAkisi, subeUcu, ayarlar
subeUcu                    11   raporlama
katalogKoprusu              7   oduncAkisi
oduncAkisi                  8   subeUcu, raporlama, ucretHesabi
ucretHesabi                 4   subeUcu
uyelikKaydi                 3   subeUcu, oduncAkisi
kimlikKoprusu               2   subeUcu, uyelikKaydi
bildirimKuyrugu             5   oduncAkisi
raporlama                   6   subeUcu
depoErisimi                 3   oduncAkisi
olayGunlugu                 4   oduncAkisi
ayarlar                     4   subeUcu, oduncAkisi, raporlama, ucretHesabi
```

#### Kuralı Sayıya Çeviren İşlev

Aynı kural iki ayrı sayıya çevrilebilir. **F1 — yüzey büyüklüğü** en ucuz çeviridir: modülün dışa
açtığı sembolleri sayar ve tek dosya okur. **F2 — ölü yüzey oranı** daha pahalıdır: dışa açılan
sembollerin kaç tanesinin hiçbir modül tarafından ithal edilmediğini bulmak için bütün kod tabanını
okur. İkisi de aynı kuralın adayıdır; hangisinin daha iyi olduğu ancak bilinen bir ihlal kümesinde
sınanarak söylenir.

```js
// uygunluk.mjs — bir mimari kurali sayiya ceviren iki uygunluk islevi ve esik taramasi
import { readFileSync, readdirSync } from "node:fs";

const DIZIN = "modul";
const dosya = readdirSync(DIZIN);

// >>> F1  yuzey buyuklugu: modulun kendi dosyasi yeter
function F1(ad) {
  const t = readFileSync(`${DIZIN}/${ad}.mjs`, "utf8");
  return [...t.matchAll(/^export const (\w+)/gm)].length;
}
// <<< F1

// >>> F2  olu yuzey orani: disa acilan sembollerin kac tanesi hic ithal edilmiyor
function F2(ad) {
  const acik = new Set([...readFileSync(`${DIZIN}/${ad}.mjs`, "utf8")
    .matchAll(/^export const (\w+)/gm)].map((m) => m[1]));
  const kullanilan = new Set();
  for (const d of dosya) {
    for (const m of readFileSync(`${DIZIN}/${d}`, "utf8")
      .matchAll(/^import \{ ([^}]+) \} from "\.\/(\w+)\.mjs"/gm)) {
      if (m[2] === ad) for (const s of m[1].split(", ")) kullanilan.add(s);
    }
  }
  return (acik.size - kullanilan.size) / acik.size;
}
// <<< F2

// YN5: mimar on iki modulu elle inceledi ve besini gercek yuzey sizintisi olarak isaretledi.
// subeUcu ve ucretHesabi olu sembol tasiyor; bildirimKuyrugu ve depoErisimi ic ayrintiyi aciyor;
// katalogKoprusu yedi sembolunu tek tuketiciye acarak ic temsilini disari tasiriyor.
const SIZINTI = new Set(["subeUcu", "katalogKoprusu", "ucretHesabi", "bildirimKuyrugu", "depoErisimi"]);
const ad = dosya.map((d) => d.replace(".mjs", ""));

console.log(`${"modul".padEnd(17)}${"F1 yuzey".padStart(10)}${"F2 olu oran".padStart(13)}${"mimar etiketi".padStart(15)}`);
for (const a of ad) {
  console.log(`${a.padEnd(17)}${String(F1(a)).padStart(10)}${F2(a).toFixed(2).padStart(13)}` +
    `${(SIZINTI.has(a) ? "sizinti" : "uygun").padStart(15)}`);
}

function tara(islev, esikler, etiket) {
  console.log(`\n${etiket.padEnd(12)}${"isaretlenen".padStart(13)}${"yakalanan".padStart(11)}` +
    `${"kacan".padStart(7)}${"yanlis alarm".padStart(14)}`);
  for (const e of esikler) {
    const isaret = ad.filter((a) => islev(a) > e);
    const y = isaret.filter((a) => SIZINTI.has(a)).length;
    console.log(`${`esik > ${e}`.padEnd(12)}${String(isaret.length).padStart(13)}` +
      `${`${y}/${SIZINTI.size}`.padStart(11)}${String(SIZINTI.size - y).padStart(7)}` +
      `${String(isaret.length - y).padStart(14)}`);
  }
}
tara(F1, [2, 3, 4, 5, 6, 7, 8, 10], "F1 yuzey");
tara(F2, [0.2, 0.4, 0.5, 0.6, 0.7], "F2 olu oran");

// kuralin kendi maliyeti: satir, dosya okuma, kosum
const kaynak = readFileSync("uygunluk.mjs", "utf8").split("\n");
const satir = (m) => {
  const b = kaynak.findIndex((l) => l.startsWith(`// >>> ${m}`));
  return kaynak.findIndex((l) => l.startsWith(`// <<< ${m}`)) - b - 1;
};
const toplamSatir = ad.reduce((s, a) => s + readFileSync(`${DIZIN}/${a}.mjs`, "utf8").split("\n").length, 0);
console.log(`\n${"islev".padEnd(12)}${"kural satiri".padStart(14)}${"dosya okuma".padStart(13)}` +
  `${"taranan satir".padStart(15)}`);
console.log(`${"F1".padEnd(12)}${String(satir("F1")).padStart(14)}${String(ad.length).padStart(13)}` +
  `${String(toplamSatir).padStart(15)}`);
console.log(`${"F2".padEnd(12)}${String(satir("F2")).padStart(14)}` +
  `${String(ad.length * (1 + dosya.length)).padStart(13)}${String((1 + ad.length) * toplamSatir).padStart(15)}`);
console.log("kosum suresi ortama baglidir; burada ortamdan bagimsiz nicelikler basilir " +
  `(F2 / F1 okuma orani ${(ad.length * (1 + dosya.length)) / ad.length}x)`);
```

```
modul              F1 yuzey  F2 olu oran  mimar etiketi
ayarlar                   4         0.00          uygun
bildirimKuyrugu           5         0.80        sizinti
depoErisimi               3         0.67        sizinti
katalogKoprusu            7         0.00        sizinti
kimlikKoprusu             2         0.00          uygun
oduncAkisi                8         0.13          uygun
olayGunlugu               4         0.75          uygun
ortakBicim                9         0.33          uygun
raporlama                 6         0.00          uygun
subeUcu                  11         0.73        sizinti
ucretHesabi               4         0.75        sizinti
uyelikKaydi               3         0.00          uygun

F1 yuzey      isaretlenen  yakalanan  kacan  yanlis alarm
esik > 2               11        5/5      0             6
esik > 3                9        4/5      1             5
esik > 4                6        3/5      2             3
esik > 5                5        2/5      3             3
esik > 6                4        2/5      3             2
esik > 7                3        1/5      4             2
esik > 8                2        1/5      4             1
esik > 10               1        1/5      4             0

F2 olu oran   isaretlenen  yakalanan  kacan  yanlis alarm
esik > 0.2              6        4/5      1             2
esik > 0.4              5        4/5      1             1
esik > 0.5              5        4/5      1             1
esik > 0.6              5        4/5      1             1
esik > 0.7              4        3/5      2             1

islev         kural satiri  dosya okuma  taranan satir
F1                       4           12            106
F2                      12          156           1378
kosum suresi ortama baglidir; burada ortamdan bagimsiz nicelikler basilir (F2 / F1 okuma orani 13x)
```

#### Eşiğin Okunması

F1 taraması eşiğin ne olduğunu çıplak gösterir. En gevşek uçta (`> 2`) beş sızıntının beşi de
yakalanır ve hiçbiri kaçmaz — ama on iki modülün on biri işaretlenir ve bunların altısı yanlış
alarmdır. En sıkı uçta (`> 10`) yanlış alarm sıfırlanır ve dört sızıntı kaçar. Arada hiçbir eşik
yoktur ki hem kaçırma hem yanlış alarm birlikte düşük olsun: `> 4` eşiği üç yakalar, iki kaçırır ve
üç yanlış alarm verir. **F1 kaçırma ile yanlış alarmı birbirinden ayıramaz, çünkü ölçtüğü sayı
kuralın sorduğu şeye yaklaşık bile değildir** — dokuz sembolünün altısı kullanılan `ortakBicim`
ile üç sembolünün ikisi ölü olan `depoErisimi` aynı ekseni paylaşır.

F2 taraması aynı ihlal kümesinde farklı bir yerde durur. `> 0.4` ile `> 0.6` arasında sonuç
değişmez: dört yakalanan, bir kaçan, bir yanlış alarm. Bu düzlük iyi bir işaret sayılır — eşiğin
tam değeri sonucu belirlemiyorsa kural eşiğe aşırı duyarlı değildir. F2'nin tek kaçırdığı
`katalogKoprusu`dur ve nedeni tanımın kendisindedir: yedi sembolünün yedisi de ithal edilir, yani
ölü yüzey sıfırdır; mimarın sızıntı demesinin nedeni sembollerin kullanılmaması değil, hepsinin tek
tüketiciye açılmış olmasıdır. F2'nin tek yanlış alarmı `olayGunlugu`dur: dört sembolünün üçü
grafiğin dışındaki işletim araçları için açıktır ve denetleyici o araçları görmez.

**Kural kodun içinde tanımlanır, kuralın kendisi ise koddan çıkarılamaz.** F2, mimarın etiketine
F1'den çok daha yakındır ama ikisi de yaklaşıktır; hiçbir eşik `katalogKoprusu` ile `olayGunlugu`
çiftini birden doğru sınıflandırmaz.

#### Kuralın Kendi Maliyeti

Son tablo kuralın bedelidir. F1 dört satırdır ve tam koşumda on iki dosya, 106 satır okur. F2 on
iki satırdır ve tam koşumda 156 dosya, 1.378 satır okur — her modül için bütün dizini taradığından
okuma **on üç katına** çıkar. Koşum süresi ortama bağlı olduğu için basılan nicelikler ortamdan
bağımsız olanlardır: okunan dosya ve taranan satır. Modül sayısı arttığında F2'nin okuması karesel
büyür, F1'inki doğrusal.

Buradan çıkan sonuç, kuralı ucuzlatmanın yolu ölçüyü bozmadan okuma sayısını düşürmektir: F2 bütün
dosyaları modül başına değil bir kez okuyup ithal haritasını bellekte kurabilir. Ama bu ders için
sayılması gereken asıl bedel koşum değil, **bakımdır**: F2 ithal biçimini ayrıştırdığı için ithal
söz diziminin her yeni biçimi kuralı bozar; F1'in ayrıştırdığı tek şey `export` satırıdır.

#### Yanlış Alarmın Ürettiği Davranış

Yanlış alarm bir sayı değil, bir davranıştır. Bir kural yanlış alarm verdiğinde ekip önce inceler,
sonra istisna ekler, sonunda kuralı kapatır. Aşağıdaki blok bunu modeller.

```js
// devre.mjs — yanlis alarmin ekipte urettigi davranis: kural ucuncu alarmda devre disi kalir
import { readFileSync, readdirSync } from "node:fs";

const DIZIN = "modul";
const dosya = readdirSync(DIZIN);
const acikSembol = (a) => [...readFileSync(`${DIZIN}/${a}.mjs`, "utf8")
  .matchAll(/^export const (\w+)/gm)].map((m) => m[1]);
const F1 = (a) => acikSembol(a).length;
const F2 = (a) => {
  const acik = new Set(acikSembol(a));
  const kullanilan = new Set();
  for (const d of dosya) {
    for (const m of readFileSync(`${DIZIN}/${d}`, "utf8")
      .matchAll(/^import \{ ([^}]+) \} from "\.\/(\w+)\.mjs"/gm)) {
      if (m[2] === a) for (const s of m[1].split(", ")) kullanilan.add(s);
    }
  }
  return (acik.size - kullanilan.size) / acik.size;
};
const SIZINTI = new Set(["subeUcu", "katalogKoprusu", "ucretHesabi", "bildirimKuyrugu", "depoErisimi"]);

// YN6: yirmi yayimin her biri tek bir module dokunur ve kural yalniz dokunulan modulde kosar.
// YN7: ekip ucuncu yanlis alarmdan sonra kurali devre disi birakir (esik gevsetme yerine kapatma).
const YAYIM = ["ayarlar", "subeUcu", "oduncAkisi", "raporlama", "ucretHesabi", "ortakBicim",
  "katalogKoprusu", "olayGunlugu", "depoErisimi", "oduncAkisi", "bildirimKuyrugu", "uyelikKaydi",
  "raporlama", "subeUcu", "ayarlar", "kimlikKoprusu", "ucretHesabi", "ortakBicim",
  "depoErisimi", "katalogKoprusu"];

function kosu(islev, esik) {
  let alarm = 0, acik = true, yakalanan = 0, kacan = 0, kapandi = 0, kapandiktanSonra = 0;
  YAYIM.forEach((m, i) => {
    if (!acik) { if (SIZINTI.has(m)) { kacan += 1; kapandiktanSonra += 1; } return; }
    const isaret = islev(m) > esik;
    if (isaret && SIZINTI.has(m)) yakalanan += 1;
    else if (!isaret && SIZINTI.has(m)) kacan += 1;
    else if (isaret) {
      alarm += 1;
      if (alarm === 3) { acik = false; kapandi = i + 1; }
    }
  });
  return { alarm, yakalanan, kacan, kapandi, kapandiktanSonra };
}

const ihlalliYayim = YAYIM.filter((m) => SIZINTI.has(m)).length;
console.log(`${YAYIM.length} yayim, ${ihlalliYayim} tanesi sizintili bir module dokunuyor`);
console.log(`\n${"kural".padEnd(16)}${"yanlis alarm".padStart(14)}${"kapandigi yayim".padStart(17)}` +
  `${"yakalanan".padStart(11)}${"kacan".padStart(7)}${"kapandiktan sonra kacan".padStart(25)}`);
for (const [etiket, islev, esik] of [["F1 esik > 3", F1, 3], ["F2 esik > 0.5", F2, 0.5]]) {
  const r = kosu(islev, esik);
  console.log(`${etiket.padEnd(16)}${String(r.alarm).padStart(14)}` +
    `${(r.kapandi ? String(r.kapandi) : "kapanmadi").padStart(17)}` +
    `${`${r.yakalanan}/${ihlalliYayim}`.padStart(11)}${String(r.kacan).padStart(7)}` +
    `${String(r.kapandiktanSonra).padStart(25)}`);
}

// YN8: cevrilemeyen kural icin yerine konan sey her ucuncu sembolun elle okunmasidir
const sembol = dosya.flatMap((d) => acikSembol(d.replace(".mjs", "")));
const ornek = sembol.filter((_, i) => i % 3 === 2);
console.log(`\ncevrilemeyen kural ("disa acilan ad modulun isini anlatir"): ` +
  `${sembol.length} sembolun ${ornek.length} tanesi elle okunur, ${sembol.length - ornek.length} tanesi okunmaz`);
```

```
20 yayim, 9 tanesi sizintili bir module dokunuyor

kural             yanlis alarm  kapandigi yayim  yakalanan  kacan  kapandiktan sonra kacan
F1 esik > 3                  3                4        1/9      8                        8
F2 esik > 0.5                1        kapanmadi        7/9      2                        0

cevrilemeyen kural ("disa acilan ad modulun isini anlatir"): 66 sembolun 22 tanesi elle okunur, 44 tanesi okunmaz
```

Sonuç, eşik taramasının tek başına anlattığından farklıdır. F1 eşiği `> 3` seçilerek kurulduğunda
tarama tablosuna göre dört sızıntıyı yakalıyordu; yirmi yayımlık koşumda ise **dördüncü yayımda
kapanır** ve kalan on altı yayım boyunca hiçbir şeye bakmaz. Dokuz ihlalli yayımın yalnız biri
yakalanır, sekizi kaçar ve sekizinin sekizi de kural kapandıktan sonradır. F2 aynı koşumda tek
yanlış alarm verir, hiç kapanmaz ve dokuz ihlalli yayımın yedisini yakalar.

Aradaki fark eşik seçiminden değil, **yanlış alarm oranından** doğar. Bir kural kapatıldığı andan
sonra ölçtüğü değer sıfırdır; bu yüzden kuralın kalitesi tek koşumdaki yakalama sayısıyla değil,
ekipte kaç yayım boyunca açık kaldığıyla ölçülür. Kuralın çevrilemeyen kısmı ise adıyla durur: dışa
açılan adın modülün işini anlatıp anlatmadığını hiçbir işlev söylemez ve yerine konan örneklem, 66
sembolün 22'sini okur; 44'ü hiç okunmaz.

#### Özet

- Aynı mimari kural iki uygunluk işlevine çevrildi: F1 yüzey büyüklüğü (dışa açılan sembol sayısı),
  F2 ölü yüzey oranı (hiç ithal edilmeyen sembollerin payı); ikisi de gerçek modül dosyalarını okur.
- F1'in eşik taramasında hiçbir eşik iyi değildir: `> 2` beş sızıntının beşini yakalar ve 6 yanlış
  alarm verir, `> 10` yanlış alarmı sıfırlar ve 4 sızıntıyı kaçırır.
- F2 aynı ihlal kümesinde `> 0.4` ile `> 0.6` arasında değişmez (4 yakalanan, 1 kaçan, 1 yanlış
  alarm) ve eşiğe duyarsızlık kuralın sağlamlık işaretidir; iki hatası da tanımdan doğar —
  `katalogKoprusu`nu kaçırır (yedi sembolü de kullanılıyor), `olayGunlugu`na yanlış alarm verir
  (üç sembolü grafiğin dışındaki araçlar için açık).
- Kuralın bedeli F1 için 4 satır, 12 dosya ve 106 satır okuma; F2 için 12 satır, 156 dosya ve
  1.378 satır okumadır; fark 13 kattır ve modül sayısıyla karesel büyür.
- Yirmi yayımlık koşumda F1 üçüncü yanlış alarmında dördüncü yayımda kapanır ve 9 ihlalli yayımın
  8'ini kaçırır; F2 tek yanlış alarmla açık kalır ve 7'sini yakalar.
- Çevrilemeyen kural adıyla yazıldı ("dışa açılan ad modülün işini anlatır") ve yerine konan
  örneklem 66 sembolün 22'sini okur.

#### Sonraki Adım

Bu dersin uygunluk işlevi tek bir modülün yüzeyine baktı ve komşularını yalnız sembol sayarken
kullandı. Mimari kuralların bir bölümü ise tek modülde hiç görünmez: hangi modülün hangi modülü
ithal edebileceği, kod tabanının **grafiğinde** durur ve ancak grafiğin tamamı okunarak
denetlenebilir. Sonraki ders bu kuralı yazar: gerçek bir ithal grafiği üzerinde koşan bir katman
denetleyicisi kurulur ve asıl soru denetleyicinin değil kuralın kendisidir — aynı katman kuralı
izin listesi biçiminde de yasak listesi biçiminde de yazılabilir. İki biçim aynı grafikte
koşturulur ve üç sayı karşılaştırılır: hangisi daha az yanlış alarm veriyor, hangisi kaç ihlali
kaçırıyor ve kod tabanına yeni bir modül eklendiğinde kural kaç yerde güncelleniyor.

### Bağımlılık ve Katman Denetimi

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/bagimlilik-ve-katman-denetimi

Yapısal mimari kuralların gerçek bir ithal grafiği üzerinde sınanması: aynı katman kuralının izin listesi ve yasak listesi biçiminde yazılması, beş kural biçiminin bilinen ihlal kümesinde yakalanan, kaçan ve yanlış alarm sayılarının karşılaştırılması, süreli istisnaların yaşlanması ve yeni bir modül eklendiğinde kuralın kaç yerde güncellendiğinin sayılması.

Önceki dersin uygunluk işlevi tek bir modülün dosyasına baktı. Mimari kuralların bir bölümü ise tek
dosyada görünmez: hangi modülün hangi modülü ithal edebileceği ancak grafiğin tamamı okunarak
denetlenir. **Katman denetimi** ve **bağımlılık denetimi** bu tür kuralların adıdır ve ikisi de aynı
veriyi okur — kod tabanının ithal grafiği.

Bu grafiğin kendisi daha önce ölçüldü: servis sınırları kursu aynı türden bir ithal grafiğinden
sınır ihlallerini saymıştı. Buradaki soru o sayım değildir. **Buradaki soru kuralın kendisidir.**
Aynı katman kuralı en az iki biçimde yazılabilir — izin listesi ("şu kenar türleri serbesttir") ve
yasak listesi ("şu kenar türleri yasaktır") — ve iki biçim aynı grafikte farklı sonuç verir. Ders
bu farkı üç sayıyla ölçer: hangisi daha az yanlış alarm veriyor, hangisi kaç ihlali kaçırıyor,
kod tabanına yeni bir modül girdiğinde kural kaç yerde güncelleniyor. Through-line **bölgesel
kütüphane ağıdır** ve kurgudur.

#### Denetlenecek İthal Grafiği

**YN9 — kod tabanı on üç modülden oluşur, her modül kendi katmanını kaynağında bildirir ve
katmanlar giriş, uygulama, alan, altyapı ve ortak olmak üzere beştir.** Gerekçe: katman denetimi
ancak her modülün bir katmana atanmış olduğu bir kod tabanında koşabilir; katman bildiriminin
kaynakta durması, denetleyicinin dışarıdan bir haritaya bağlı kalmamasını sağlar.

```js
// kur.mjs — bolgesel kutuphane agi kod tabanini katmanlariyla diske yazar
import { mkdirSync, writeFileSync } from "node:fs";

const KATMAN = {
  subeUcu: "giris", personelUcu: "giris",
  oduncAkisi: "uygulama", ucretAkisi: "uygulama", uyelikAkisi: "uygulama",
  oduncKurali: "alan", ucretKurali: "alan", katalogKurali: "alan",
  depoErisimi: "altyapi", katalogKoprusu: "altyapi", kimlikKoprusu: "altyapi",
  olayGunlugu: "altyapi", ortakBicim: "ortak",
};

const KENAR = [
  "subeUcu->oduncAkisi", "subeUcu->ucretAkisi", "subeUcu->ortakBicim",
  "personelUcu->uyelikAkisi", "personelUcu->oduncAkisi", "personelUcu->ortakBicim",
  "oduncAkisi->oduncKurali", "oduncAkisi->katalogKurali", "oduncAkisi->depoErisimi",
  "oduncAkisi->katalogKoprusu", "oduncAkisi->ortakBicim",
  "ucretAkisi->ucretKurali", "ucretAkisi->depoErisimi", "ucretAkisi->ortakBicim",
  "uyelikAkisi->kimlikKoprusu", "uyelikAkisi->ortakBicim",
  "oduncKurali->ortakBicim", "ucretKurali->ortakBicim", "katalogKurali->ortakBicim",
  "depoErisimi->ortakBicim", "olayGunlugu->ortakBicim", "katalogKoprusu->ortakBicim",
  "katalogKoprusu->katalogKurali", "kimlikKoprusu->ortakBicim",
  "subeUcu->katalogKoprusu", "ucretAkisi->oduncAkisi",          // istisna kaydi olan iki kenar
  "oduncKurali->depoErisimi", "ucretKurali->katalogKoprusu", "depoErisimi->oduncAkisi",
  "subeUcu->depoErisimi", "subeUcu->katalogKurali", "ortakBicim->olayGunlugu",
  "oduncAkisi->kimlikKoprusu", "ucretAkisi->katalogKoprusu",
];

mkdirSync("kod", { recursive: true });
for (const ad of Object.keys(KATMAN)) {
  const ithal = KENAR.filter((k) => k.split("->")[0] === ad)
    .map((k) => `import { ${k.split("->")[1]}Islem } from "./${k.split("->")[1]}.mjs";`);
  writeFileSync(`kod/${ad}.mjs`, `// katman: ${KATMAN[ad]}\n${ithal.join("\n")}\n` +
    `export const ${ad}Islem = (x) => x;\n`);
}

const K = ["giris", "uygulama", "alan", "altyapi", "ortak"];
const sayi = {};
for (const k of KENAR) {
  const [a, b] = k.split("->");
  sayi[`${KATMAN[a]}->${KATMAN[b]}`] = (sayi[`${KATMAN[a]}->${KATMAN[b]}`] ?? 0) + 1;
}
console.log(`${Object.keys(KATMAN).length} modul dosyasi, ${KENAR.length} ithal kenari`);
console.log(`\nkatmandan katmana kenar sayisi (satir: ithal eden, sutun: ithal edilen)`);
console.log(`${"".padEnd(10)}${K.map((k) => k.padStart(9)).join("")}`);
for (const a of K) {
  console.log(`${a.padEnd(10)}${K.map((b) => String(sayi[`${a}->${b}`] ?? 0).padStart(9)).join("")}`);
}
```

```
13 modul dosyasi, 34 ithal kenari

katmandan katmana kenar sayisi (satir: ithal eden, sutun: ithal edilen)
              giris uygulama     alan  altyapi    ortak
giris             0        4        1        2        2
uygulama          0        1        3        6        3
alan              0        0        0        2        3
altyapi           0        1        1        0        4
ortak             0        0        0        1        0
```

Matris kuralın hangi hücrelerde çiğnendiğini daha kural yazılmadan gösterir: `alan` satırında
altyapıya iki kenar, `giriş` satırında alana bir ve altyapıya iki kenar, `ortak` satırında
altyapıya bir kenar vardır. Kuralı yazmak bu hücreleri adlandırmaktır — ve adlandırmanın iki ayrı
yolu vardır.

#### Aynı Kuralın Beş Biçimi

Denetleyici dosyaları okur, `// katman:` satırından katmanı ve `from "./..."` satırlarından kenarı
çıkarır. Aynı kural beş biçimde yazılır: katman granülerliğinde izin listesi, buna eklenen istisna
listesi (süresi denetlenen ve denetlenmeyen), modül granülerliğinde izin listesi ve katman
granülerliğinde yasak listesi.

**YN10 — mimar otuz dört kenarı elle okudu ve dokuzunu gerçek ihlal olarak işaretledi; iki kenarın
süreli istisna kaydı vardır ve birinin süresi dolmuştur.** Gerekçe: yakalama, kaçırma ve yanlış
alarm ancak elle etiketlenmiş bir kümeye karşı sayılabilir; istisna kayıtları da bu kümenin
parçasıdır, çünkü onaylanmış bir kenarı ihlal saymak yanlış alarmdır.

```js
// katman.mjs — kod/ dizinindeki gercek ithal grafigini okur; ayni kural bes bicimde kosturulur
import { readFileSync, readdirSync, writeFileSync } from "node:fs";

function grafik(dizin = "kod") {
  const katman = {}, kenar = [];
  for (const d of readdirSync(dizin)) {
    const ad = d.replace(".mjs", "");
    const t = readFileSync(`${dizin}/${d}`, "utf8");
    katman[ad] = t.match(/^\/\/ katman: (\w+)$/m)[1];
    for (const m of t.matchAll(/from "\.\/(\w+)\.mjs"/g)) kenar.push(`${ad}->${m[1]}`);
  }
  return { katman, kenar };
}
const { katman, kenar } = grafik();
const kat = (k) => `${katman[k.split("->")[0]]}->${katman[k.split("->")[1]]}`;

// YN10: mimar otuz dort kenari elle okudu; asagidaki dokuzu gercek ihlaldir. Istisna kaydi olan
// subeUcu->katalogKoprusu gecerlidir; ucretAkisi->oduncAkisi istisnasinin suresi dolmustur.
const IHLAL = new Set([
  "ucretAkisi->oduncAkisi",
  "oduncKurali->depoErisimi", "ucretKurali->katalogKoprusu", "depoErisimi->oduncAkisi",
  "subeUcu->depoErisimi", "subeUcu->katalogKurali", "ortakBicim->olayGunlugu",
  "oduncAkisi->kimlikKoprusu", "ucretAkisi->katalogKoprusu",
]);
const CEYREK = 4;
const ISTISNA = [                                   // kenar | biten ceyrek | gerekce
  ["subeUcu->katalogKoprusu", 5, "goc donemi: sube ucu eski katalog koprusunu okuyor"],
  ["ucretAkisi->oduncAkisi", 2, "gecici: ucret akisi odunc akisindan okuyor"],
];

const IZIN_KATMAN = ["giris->uygulama", "giris->ortak", "uygulama->alan", "uygulama->altyapi",
  "uygulama->ortak", "alan->ortak", "altyapi->alan", "altyapi->ortak"];
const YASAK_KATMAN = ["alan->altyapi", "alan->uygulama", "alan->giris",
  "altyapi->uygulama", "altyapi->giris", "uygulama->giris"];
const IZIN_MODUL = [
  "subeUcu->oduncAkisi", "subeUcu->ucretAkisi", "subeUcu->ortakBicim",
  "personelUcu->uyelikAkisi", "personelUcu->oduncAkisi", "personelUcu->ortakBicim",
  "oduncAkisi->oduncKurali", "oduncAkisi->katalogKurali", "oduncAkisi->depoErisimi",
  "oduncAkisi->katalogKoprusu", "oduncAkisi->ortakBicim",
  "ucretAkisi->ucretKurali", "ucretAkisi->depoErisimi", "ucretAkisi->ortakBicim",
  "uyelikAkisi->kimlikKoprusu", "uyelikAkisi->ortakBicim",
  "oduncKurali->ortakBicim", "ucretKurali->ortakBicim", "katalogKurali->ortakBicim",
  "depoErisimi->ortakBicim", "olayGunlugu->ortakBicim", "katalogKoprusu->ortakBicim",
  "katalogKoprusu->katalogKurali", "kimlikKoprusu->ortakBicim",
  "subeUcu->katalogKoprusu",
];

writeFileSync("izin_modul.json", JSON.stringify(IZIN_MODUL));

const izinliKenar = (sureDenetle) => new Set(ISTISNA
  .filter(([, bitis]) => !sureDenetle || bitis >= CEYREK).map(([k]) => k));
const BICIM = {
  "izin-katman":               { n: IZIN_KATMAN.length, f: (k) => !IZIN_KATMAN.includes(kat(k)) },
  "izin-katman + istisna":     { n: IZIN_KATMAN.length + ISTISNA.length,
    f: (k) => !IZIN_KATMAN.includes(kat(k)) && !izinliKenar(false).has(k) },
  "izin-katman + sureli ist.": { n: IZIN_KATMAN.length + ISTISNA.length,
    f: (k) => !IZIN_KATMAN.includes(kat(k)) && !izinliKenar(true).has(k) },
  "izin-modul":                { n: IZIN_MODUL.length, f: (k) => !IZIN_MODUL.includes(k) },
  "yasak-katman":              { n: YASAK_KATMAN.length, f: (k) => YASAK_KATMAN.includes(kat(k)) },
};

console.log(`${kenar.length} kenar okundu; bilinen ihlal ${IHLAL.size}, istisna kaydi ${ISTISNA.length} ` +
  `(${ISTISNA.filter(([, b]) => b < CEYREK).length} tanesinin suresi dolmus)`);
console.log(`\n${"kural bicimi".padEnd(27)}${"kural satiri".padStart(13)}${"isaretlenen".padStart(13)}` +
  `${"yakalanan".padStart(11)}${"kacan".padStart(7)}${"yanlis alarm".padStart(14)}`);
for (const [ad, b] of Object.entries(BICIM)) {
  const isaret = kenar.filter(b.f);
  const y = isaret.filter((k) => IHLAL.has(k)).length;
  console.log(`${ad.padEnd(27)}${String(b.n).padStart(13)}${String(isaret.length).padStart(13)}` +
    `${`${y}/${IHLAL.size}`.padStart(11)}${String(IHLAL.size - y).padStart(7)}` +
    `${String(isaret.length - y).padStart(14)}`);
}

console.log(`\nkacan ihlaller (kural bicimi basina):`);
for (const [ad, b] of Object.entries(BICIM)) {
  const kacan = [...IHLAL].filter((k) => !b.f(k));
  console.log(`  ${ad.padEnd(27)}${kacan.length ? kacan.map((k) => `${k} (${kat(k)})`).join("; ") : "-"}`);
}
```

```
34 kenar okundu; bilinen ihlal 9, istisna kaydi 2 (1 tanesinin suresi dolmus)

kural bicimi                kural satiri  isaretlenen  yakalanan  kacan  yanlis alarm
izin-katman                            8            8        7/9      2             1
izin-katman + istisna                 10            6        6/9      3             0
izin-katman + sureli ist.             10            7        7/9      2             0
izin-modul                            25            9        9/9      0             0
yasak-katman                           6            3        3/9      6             0

kacan ihlaller (kural bicimi basina):
  izin-katman                oduncAkisi->kimlikKoprusu (uygulama->altyapi); ucretAkisi->katalogKoprusu (uygulama->altyapi)
  izin-katman + istisna      ucretAkisi->oduncAkisi (uygulama->uygulama); oduncAkisi->kimlikKoprusu (uygulama->altyapi); ucretAkisi->katalogKoprusu (uygulama->altyapi)
  izin-katman + sureli ist.  oduncAkisi->kimlikKoprusu (uygulama->altyapi); ucretAkisi->katalogKoprusu (uygulama->altyapi)
  izin-modul                 -
  yasak-katman               ucretAkisi->oduncAkisi (uygulama->uygulama); subeUcu->depoErisimi (giris->altyapi); subeUcu->katalogKurali (giris->alan); ortakBicim->olayGunlugu (ortak->altyapi); oduncAkisi->kimlikKoprusu (uygulama->altyapi); ucretAkisi->katalogKoprusu (uygulama->altyapi)
```

#### İzin Listesi ile Yasak Listesinin Ayrımı

Tablo yaygın bir beklentiyi bozar. **Yasak listesi sıfır yanlış alarm verir ve dokuz ihlalin altısını
kaçırır.** Nedeni biçimin kendisindedir: yasak listesi yalnız *sayılan* kenar türlerini görür.
Listede `alan->altyapi` ve `altyapi->uygulama` yazılıdır, bu yüzden üç ihlal yakalanır; ama
`giris->altyapi`, `giris->alan`, `ortak->altyapi` ve aynı katman içindeki `uygulama->uygulama`
listede yoktur ve o kenarlar hiç görülmez. Yasak listesi ne kadar eksikse o kadar sessizdir ve
eksik olduğu hiçbir yerde belli olmaz.

**İzin listesi ters çalışır: listede olmayan her şey ihlaldir.** Katman granülerliğindeki izin
listesi sekiz satırla dokuz ihlalin yedisini yakalar. Kaçırdığı ikisi kuralın ifade gücünün
sınırıdır: `oduncAkisi->kimlikKoprusu` ve `ucretAkisi->katalogKoprusu` kenarları katman düzeyinde
serbest olan `uygulama->altyapi` çiftine düşer; ikisinin de yasak olduğunu söyleyen kural katmanla
değil modül adıyla yazılır. Buna karşılık izin listesinin bir yanlış alarmı vardır ve o alarm
onaylanmış bir göç kenarını işaretler.

İstisna listesi bu yanlış alarmı kaldırır ama kendi kusurunu getirir. Süresi denetlenmeyen istisna
listesinde yakalama 7'den 6'ya düşer: süresi iki çeyrek önce dolmuş bir istisna hâlâ bir gerçek
ihlali gizlemektedir. Süre denetlendiğinde yakalama 7'ye döner ve yanlış alarm sıfırda kalır.
**Bir istisna listesi, süresi denetlenmediği sürece kuralın kendisini aşındırır** — ve aşınmanın
büyüklüğü tam olarak süresi dolmuş istisna sayısı kadardır.

Modül granülerliğindeki izin listesi tabloda kusursuzdur: dokuz ihlalin dokuzu yakalanır, hiç
kaçan ve hiç yanlış alarm yoktur. Bedeli satır sayısında görünür — sekiz satır yerine yirmi beş
satır, yani kural artık mimarinin bir özeti değil, grafiğin tam kopyasıdır. Asıl bedel ise bu
tabloda hiç görünmez; sonraki koşum onu ölçer.

#### Yeni Modül Girdiğinde

Kuralın bakım maliyeti kod tabanı durduğu sürece sıfırdır. Aşağıdaki blok kod tabanına yeni bir
modül ekler ve aynı kuralları güncellenmemiş hâlleriyle koşturur.

```js
// yeni.mjs — kod tabanina yeni bir modul eklenir; her kural bicimi kac yerde guncelleniyor
import { readFileSync, readdirSync, writeFileSync } from "node:fs";

// YN11: sonraki ceyrekte kod tabanina tek bir yeni modul girer ve alti yeni ithal kenari acar
const YENI = "rezervasyonAkisi";
const YENI_KENAR = [                                            // besi kurala uygun, biri ihlal
  `subeUcu->${YENI}`, `personelUcu->${YENI}`, `${YENI}->oduncKurali`,
  `${YENI}->depoErisimi`, `${YENI}->ortakBicim`, `${YENI}->personelUcu`,
];
writeFileSync(`kod/${YENI}.mjs`, `// katman: uygulama\n` +
  YENI_KENAR.filter((k) => k.startsWith(`${YENI}->`))
    .map((k) => `import { ${k.split("->")[1]}Islem } from "./${k.split("->")[1]}.mjs";`).join("\n") +
  `\nexport const ${YENI}Islem = (x) => x;\n`);
for (const k of YENI_KENAR.filter((k) => k.endsWith(`->${YENI}`))) {
  const a = k.split("->")[0];
  const t = readFileSync(`kod/${a}.mjs`, "utf8");
  writeFileSync(`kod/${a}.mjs`, t.replace(/\n/, `\nimport { ${YENI}Islem } from "./${YENI}.mjs";\n`));
}

const katman = {}, kenar = [];
for (const d of readdirSync("kod")) {
  const ad = d.replace(".mjs", "");
  const t = readFileSync(`kod/${d}`, "utf8");
  katman[ad] = t.match(/^\/\/ katman: (\w+)$/m)[1];
  for (const m of t.matchAll(/from "\.\/(\w+)\.mjs"/g)) kenar.push(`${ad}->${m[1]}`);
}
const kat = (k) => `${katman[k.split("->")[0]]}->${katman[k.split("->")[1]]}`;

const IZIN_KATMAN = ["giris->uygulama", "giris->ortak", "uygulama->alan", "uygulama->altyapi",
  "uygulama->ortak", "alan->ortak", "altyapi->alan", "altyapi->ortak"];
const YASAK_KATMAN = ["alan->altyapi", "alan->uygulama", "alan->giris",
  "altyapi->uygulama", "altyapi->giris", "uygulama->giris"];
const IZIN_MODUL_ESKI = JSON.parse(readFileSync("izin_modul.json", "utf8"));
const uygunYeni = YENI_KENAR.filter((k) => IZIN_KATMAN.includes(kat(k)));
const IZIN_MODUL_YENI = [...IZIN_MODUL_ESKI, ...uygunYeni];

const BICIM = {
  "izin-katman":         { veri: IZIN_KATMAN, f: (k) => !IZIN_KATMAN.includes(kat(k)) },
  "yasak-katman":        { veri: YASAK_KATMAN, f: (k) => YASAK_KATMAN.includes(kat(k)) },
  "izin-modul (eski)":   { veri: IZIN_MODUL_ESKI, f: (k) => !IZIN_MODUL_ESKI.includes(k) },
  "izin-modul (guncel)": { veri: IZIN_MODUL_YENI, f: (k) => !IZIN_MODUL_YENI.includes(k) },
};
const YENI_IHLAL = new Set([`${YENI}->personelUcu`]);

console.log(`yeni modul: ${YENI} (katman: uygulama), ${YENI_KENAR.length} yeni kenar; ` +
  `${uygunYeni.length} tanesi katman kuralina uygun, ${YENI_KENAR.length - uygunYeni.length} tanesi ihlal`);
console.log(`grafik simdi ${Object.keys(katman).length} modul ve ${kenar.length} kenar`);
console.log(`\n${"kural bicimi".padEnd(22)}${"kural satiri".padStart(13)}${"guncellenen satir".padStart(19)}` +
  `${"yeni ihlal yakalandi".padStart(22)}${"yeni yanlis alarm".padStart(19)}`);
for (const [ad, b] of Object.entries(BICIM)) {
  const isaret = YENI_KENAR.filter(b.f);
  const y = isaret.filter((k) => YENI_IHLAL.has(k)).length;
  const guncel = ad === "izin-modul (guncel)" ? uygunYeni.length : 0;
  console.log(`${ad.padEnd(22)}${String(b.veri.length).padStart(13)}${String(guncel).padStart(19)}` +
    `${`${y}/${YENI_IHLAL.size}`.padStart(22)}${String(isaret.length - y).padStart(19)}`);
}
console.log(`\nyeni modulun kendi dosyasinda her bicim icin ayni tek satir var: "// katman: uygulama"`);
```

```
yeni modul: rezervasyonAkisi (katman: uygulama), 6 yeni kenar; 5 tanesi katman kuralina uygun, 1 tanesi ihlal
grafik simdi 14 modul ve 40 kenar

kural bicimi           kural satiri  guncellenen satir  yeni ihlal yakalandi  yeni yanlis alarm
izin-katman                       8                  0                   1/1                  0
yasak-katman                      6                  0                   1/1                  0
izin-modul (eski)                25                  0                   1/1                  5
izin-modul (guncel)              30                  5                   1/1                  0

yeni modulun kendi dosyasinda her bicim icin ayni tek satir var: "// katman: uygulama"
```

Modül granülerliğindeki izin listesinin gizli bedeli budur. Yeni modül tek satırlık bir katman
bildirimiyle gelir; katman granülerliğindeki iki biçim hiçbir yerde güncellenmez ve yeni ihlali
yakalar. Modül granülerliğindeki izin listesi ise güncellenmediğinde **beş yanlış alarm** üretir ve
bu beş alarmın hepsi kurala tümüyle uygun kenarlar üzerinedir; güncellendiğinde alarm sıfırlanır ve
kural yirmi beş satırdan otuza çıkar. Her yeni modül kuralı beş satır büyütür.

Bu, önceki dersin devre dışı bırakma koşumuyla aynı olgudur. Kurala uygun bir değişikliği engelleyen
bir denetim, birkaç yayımdan sonra ya kapatılır ya da kural kendiliğinden onaylanan bir listeye
dönüşür. Modül granülerliğindeki izin listesi tabloda kusursuz görünürken pratikte en kırılgan
biçimdir, çünkü doğruluğu **her yeni kenarda elle güncellenmesine** bağlıdır.

Ölçülen üç biçim arasındaki seçim şu üç sayının bileşimidir: yasak listesi hiç bakım istemez ve
altı ihlal kaçırır; katman izin listesi hiç bakım istemez, iki ihlal kaçırır ve ihlalleri yakalamak
için istisna listesinin süre denetimine muhtaçtır; modül izin listesi hiçbir şey kaçırmaz ve her
modülde beş satır bakım ister. **Kuralın granülerliği yükseldikçe yakalama artar ve bakım maliyeti
onunla birlikte artar**; hiçbir biçim ikisini birden ucuzlatmaz.

#### Özet

- Katman denetimi gerçek dosyalardan okunan bir ithal grafiği üzerinde koşturuldu: 13 modül, 34
  kenar, 9 elle etiketlenmiş ihlal ve süreli 2 istisna kaydı.
- Yasak listesi biçimi 6 satırla hiç yanlış alarm vermez ama 9 ihlalin 6'sını kaçırır; kaçırdığı
  kenar türleri listede sayılmadığı için sessizce geçer.
- Katman granülerliğindeki izin listesi 8 satırla 7 ihlal yakalar, 2 kaçırır ve onaylanmış bir göç
  kenarına 1 yanlış alarm verir; kaçırdığı ikisi katmanla değil modül adıyla yazılabilen kurallardır.
- Süresi denetlenmeyen istisna listesi yakalamayı 7'den 6'ya düşürür; süresi dolmuş tek bir istisna
  bir gerçek ihlali gizler, süre denetimi eklendiğinde yakalama 7'ye döner ve yanlış alarm sıfırdır.
- Modül granülerliğindeki izin listesi 25 satırla 9/9 yakalar ve hiç yanlış alarm vermez; bedeli
  yeni modülde ortaya çıkar — güncellenmediğinde kurala uygun 5 kenara yanlış alarm verir,
  güncellendiğinde 5 satır büyür.
- Kuralın granülerliği yükseldikçe yakalama artar ve bakım maliyeti onunla birlikte artar; katman
  bildirimi kaynakta durduğu için katman biçimleri yeni modülde 0 satır güncelleme ister.

#### Sonraki Adım

Bu üç derste kurallar yazıldı, denetime çevrildi ve denetimin yakaladığı ile kaçırdığı sayıldı.
Hepsinin ortak varsayımı şudur: mimari sabittir ve kural onu korur. Oysa bu dersin son koşumu tam
tersini gösterdi — kod tabanına bir modül girdiği anda kuralın kendisi de değişmek zorunda kaldı.
Bir mimarinin ölçülmesi gereken niteliği yalnız bugün kurala uyup uymadığı değil, **değişimi ne
kadar ucuza kabul ettiğidir**. Sonraki ders bu niteliği ele alır: değişimi ucuzlatan yapı
kararlarının kendisi bir denetime çevrilebilir mi, çevrilebiliyorsa hangi sayı ölçülür ve
çevrilemeyen kısmın yerine ne konur.

### Evrimsel Mimari

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/evrimsel-mimari

Değişime dayanıklı yapının denetlenmesi: aynı üç değişikliğin bir yapıda 18, yalıtma noktalı yapıda 5 modüle dokunması, yalıtımın kalıcı olarak 5 düğüm ve 4 bağ pahalı olması, uygunluk işlevinin 8 ihlalin 7'sini yakalayıp 1 yanlış alarm vermesi ve bir bağımlılık içeri alınınca aynı kural kümesinin 5 doğru değişikliği ihlal olarak işaretlemesi.

Önceki ders bağımlılık ve katman kurallarını koşturulabilir bir denetime çevirdi: kural artık bir
belge cümlesi değil, ithal grafiği üzerinde çalışan bir yüklemdir. Ama bu kuralların hepsi
**bugünkü** yapıyı korur: bugünkü katman ayrımını dondurur ve yapının kendisi değişmek zorunda
kaldığında ya doğru değişimi ucuzlatır ya da onu ihlal olarak işaretler.

**Evrimsel mimari** (evolutionary architecture), değişimi ucuz tutan yapı kararlarını
denetlenebilir hâle getirme uğraşıdır. Kursun sorusu bir adım kayar: **değişimi ucuzlatan yapı
kararı** denetlenebilir mi? Dört ölçü alınır: dokunulan modül ve geri alma adımı; uygunluk
işlevinin yakalaması, kaçırması ve yanlış alarmı; kuralın kendi eskimesi; çevrilemeyen kısım.

#### Aynı Değişiklik Kümesi, İki Yapı

Örnek, kursun kurgu **bölgesel kütüphane ağı**dır. **YN12:** altı iç modül (şube ön yüzü, şube
terminali, ödünç, ücretlendirme, raporlama, bildirim) ve üç dış bağımlılık (dışarıdan alınmış
katalog, ayrı üyelik sistemi, belediyedeki kimlik servisi); bugünkü yapı A bunları 16 bağla
bağlar. **YN13:** bağ, iç biçime dayanıyorsa **biçim bağı**, bildirilmiş sözleşmeye dayanıyorsa
**sözleşme bağı**dır. **YN14 — sızan modül:** A'da `odunc` ile `ucret` aldıkları kaydı olduğu gibi
yeniden yayar; B'de her dış bağımlılığın önünde biçimi eşleyen bir **yalıtma noktası** (kapı) durur
ve sızan modül kalmaz. **YN15 — değişiklik kümesi:** katalog
kaydına alan eklemek, üyelik sistemini değiştirmek, ücretlendirmeyi ikiye ayırmak.

İki dosya grafı, yayılımı ve beş kuralı kuran süreç içi bir **modeldir**; ağ ya da depo yoktur.

```js
// evrim/yapi.mjs — kurgu kutuphane aginin modul grafi ve kural denetleyicisi (model). Kenar
// yazimi "bagimli>bagimlilik:tur"; "b" bicim bagi, "s" sozlesme bagi.

export const bag = (s) => s.trim().split(/\s+/).map((e) => e.split(/[>:]/));

export const YAPI_A = bag(`
  sube-onyuz>katalog:b sube-onyuz>odunc:b sube-onyuz>kimlik:b
  sube-terminal>katalog:b sube-terminal>odunc:b sube-terminal>kimlik:b
  odunc>katalog:b odunc>uyelik:b odunc>ucret:b ucret>odunc:b ucret>uyelik:b
  rapor>odunc:b rapor>ucret:b rapor>katalog:b bildirim>odunc:b bildirim>uyelik:b`);

export const YAPI_B = bag(`
  sube-onyuz>katalog-kapisi:s sube-onyuz>odunc-kapisi:s sube-onyuz>kimlik-kapisi:s
  sube-terminal>katalog-kapisi:s sube-terminal>odunc-kapisi:s sube-terminal>kimlik-kapisi:s
  odunc>katalog-kapisi:s odunc>uyelik-kapisi:s odunc>ucret-kapisi:s ucret>uyelik-kapisi:s
  rapor>odunc-kapisi:s rapor>ucret:b rapor>katalog-kapisi:s
  bildirim>odunc-kapisi:s bildirim>uyelik-kapisi:s
  katalog-kapisi>katalog:b uyelik-kapisi>uyelik:b kimlik-kapisi>kimlik:b
  odunc-kapisi>odunc:b ucret-kapisi>ucret:b`);

// Aldigi bicimi oldugu gibi yeniden yayan moduller (kaba isaret)
export const SIZAN = { A: new Set(["odunc", "ucret"]), B: new Set() };
export const UC = ["sube-onyuz", "sube-terminal"];

// "dis" ve "kapi" yapinin degil, yapilandirmanin bildirdigi seylerdir.
export const ESKI = {
  dis: ["katalog", "uyelik", "kimlik"],
  kapi: { "katalog-kapisi": "katalog", "uyelik-kapisi": "uyelik", "kimlik-kapisi": "kimlik",
    "odunc-kapisi": "odunc", "ucret-kapisi": "ucret" },
  ucIzin: ["katalog-kapisi", "odunc-kapisi", "kimlik-kapisi"],
  istisna: [["rapor", "ucret"]],
};
const { "uyelik-kapisi": _, ...KAPI_YENI } = ESKI.kapi;      // uyelik ice alindiktan sonra
export const YENI = { ...ESKI, dis: ["katalog", "kimlik"], kapi: KAPI_YENI,
  ucIzin: [...ESKI.ucIzin, "uyelik"] };

export const modul = (k) => [...new Set(k.flatMap(([a, b]) => [a, b]))];

export const uygula = (k, o) => {
  const s = (o.sil ?? []).map((x) => `${x[0]}>${x[1]}`);
  return [...k.filter(([a, b]) => !s.includes(`${a}>${b}`)), ...(o.ek ?? [])];
};

// Bicim bagi degisen bicimi tasir; sozlesme bagi ancak sozlesme degisince gecer.
export function dokunulan(kenar, sizan, dis, d) {
  const kume = new Set([d.kok]);
  for (let ek = true; ek; ) {
    ek = false;
    for (const [a, b, tur] of kenar) {
      if (kume.has(a) || !kume.has(b)) continue;
      if (tur === "b" ? b === d.kok || sizan.has(b) : d.sozlesmeDegisen.includes(b)) {
        kume.add(a); ek = true;
      }
    }
  }
  return [...kume].filter((m) => !dis.includes(m));
}

// Tek kapida kalan degisiklik bir anahtarla geri alinir.
export const geriAlma = (d, kapi) => (d.length === 1 && d[0] in kapi ? 1 : d.length);

export function dongudekiler(kenar) {
  const m = modul(kenar), u = new Map(m.map((x) => [x, new Set()]));
  for (const [a, b] of kenar) u.get(a).add(b);
  for (let d = true; d; ) {                                  // gecisli kapanis
    d = false;
    for (const a of m) for (const b of [...u.get(a)]) for (const c of u.get(b) ?? [])
      if (!u.get(a).has(c)) { u.get(a).add(c); d = true; }
  }
  return m.filter((x) => u.get(x).has(x));
}

// Uygunluk islevi: degisime aciklik
export function uygunluk(kenar, y) {
  const kapiAdi = Object.keys(y.kapi);
  const tam = y.dis.filter((x) => {
    const g = kenar.filter(([, b]) => b === x).map(([a]) => a);
    return g.length === 1 && kapiAdi.includes(g[0]);
  });
  return { dugum: modul(kenar).length, bag: kenar.length,
    "dongudeki modul": dongudekiler(kenar).length,
    "yalitilmamis dis bag": kenar.filter(([a, b]) => y.dis.includes(b) && !kapiAdi.includes(a)).length,
    "tam yalitilmis dis": `${tam.length}/${y.dis.length}` };
}

// Bes mimari kural; her biri ihlal sayisi dondurur
export function kurallar(y) {
  const kapiAdi = Object.keys(y.kapi);
  const icHedef = Object.values(y.kapi).filter((m) => !y.dis.includes(m));
  return {
    K1: (k) => k.filter(([a, b]) => y.dis.includes(b) && !kapiAdi.includes(a)).length,
    K2: (k) => dongudekiler(k).length,
    K3: (k) => k.filter(([a, b, t]) => t === "b" && icHedef.includes(b) && !kapiAdi.includes(a)
      && !y.istisna.some(([x, z]) => x === a && z === b)).length,
    K4: (k) => k.filter(([a, b]) => UC.includes(a) && !y.ucIzin.includes(b)).length,
    K5: (k) => y.dis.filter((x) => k.filter(([, b]) => b === x).length !== 1
      || !kapiAdi.includes((k.find(([, b]) => b === x) ?? [])[0])).length,
  };
}
```

```js
// evrim/olc.mjs — iki yapida ayni degisiklik kumesi, bilinen ihlal kumesi, kuralin eskimesi.
import { YAPI_A, YAPI_B, SIZAN, ESKI, YENI, bag, uygula, dokunulan, geriAlma, uygunluk, kurallar }
  from "./yapi.mjs";

const yaz = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));

const uA = uygunluk(YAPI_A, ESKI), uB = uygunluk(YAPI_B, ESKI);
yaz([-24, 8, 8], "uygunluk islevi", "A", "B");
for (const k of Object.keys(uA)) yaz([-24, 8, 8], k, uA[k], uB[k]);

const DEGISIKLIK = [
  { ad: "yeni alan (katalog)", kok: "katalog", sozlesmeDegisen: [] },
  { ad: "bagimlilik degistir (uyelik)", kok: "uyelik", sozlesmeDegisen: [] },
  { ad: "ucret'i ikiye ayir", kok: "ucret", sozlesmeDegisen: ["ucret"] },
];
const D = [-30, 14, 14, 14, 14], T = { A: [0, 0], B: [0, 0] };
console.log();
yaz(D, "degisiklik", "A: dokunulan", "A: geri alma", "B: dokunulan", "B: geri alma");
for (const d of DEGISIKLIK) {
  const s = {};
  for (const [ad, kenar, sizan] of [["A", YAPI_A, SIZAN.A], ["B", YAPI_B, SIZAN.B]]) {
    const dk = dokunulan(kenar, sizan, ESKI.dis, d), g = geriAlma(dk, ESKI.kapi);
    s[ad] = [dk.length, g]; T[ad][0] += dk.length; T[ad][1] += g;
  }
  yaz(D, d.ad, s.A[0], s.A[1], s.B[0], s.B[1]);
}
yaz(D, "toplam", T.A[0], T.A[1], T.B[0], T.B[1]);

// Bilinen ihlal kumesi: her oneri elle etiketlenmistir (model girdisi).
const ONERI = [
  ["hizli arama", "sube-onyuz>katalog:b", "ihlal"],
  ["gece aktarimi", "rapor>katalog:b", "ihlal"],
  ["borc sorgusu", "ucret>odunc:b", "ihlal"],
  ["bildirimde ucret", "bildirim>ucret:b", "ihlal"],
  ["kapidan katalog", "odunc-kapisi>katalog:b", "ihlal"],
  ["yeni kiosk", "sube-kiosk>odunc:b", "ihlal"],
  ["bildirim kimlik", "bildirim>kimlik-kapisi:s", "temiz"],
  ["rapor uyelik", "rapor>uyelik-kapisi:s", "temiz"],
  ["arsiv modulu", "arsiv>odunc-kapisi:s arsiv>katalog-kapisi:s", "temiz"],
  ["kapi zinciri", "katalog-kapisi>uyelik-kapisi:s", "ihlal"],
  ["terminal uyelik", "sube-terminal>uyelik-kapisi:s", "temiz"],
  ["bildirim uyelik", "bildirim>uyelik:b", "ihlal"],
].map(([ad, e, gercek]) => ({ ad, ek: bag(e), gercek }));

function kos(taban, oneriler, y, kapali = []) {
  const K = kurallar(y), ad = Object.keys(K).filter((x) => !kapali.includes(x));
  const temel = Object.fromEntries(ad.map((x) => [x, K[x](taban)]));
  const satir = oneriler.map((o) => {
    const aten = ad.filter((x) => K[x](uygula(taban, o)) > temel[x]);
    const sonuc = o.gercek === "ihlal" ? (aten.length ? "yakalandi" : "kacti")
      : aten.length ? "yanlis alarm" : "dogru gecti";
    return { ad: o.ad, gercek: o.gercek, aten, sonuc };
  });
  const say = (s) => satir.filter((x) => x.sonuc === s).length;
  return { satir, temel, yakalandi: say("yakalandi"), kacti: say("kacti"),
    yanlis: say("yanlis alarm"), dogru: say("dogru gecti") };
}

const R = kos(YAPI_B, ONERI, ESKI), ihlal = ONERI.filter((o) => o.gercek === "ihlal").length;
console.log(`\nB tabani: ${Object.entries(R.temel).map(([a, b]) => `${a}=${b}`).join(" ")}`);
const O = [-18, 8, 14, 14];
yaz(O, "oneri", "gercek", "atesleyen", "sonuc");
for (const s of R.satir) yaz(O, s.ad, s.gercek, s.aten.join(",") || "-", s.sonuc);
console.log(`yakalanan ${R.yakalandi}/${ihlal}, kacan ${R.kacti}, ` +
  `yanlis alarm ${R.yanlis}/${ONERI.length - ihlal} temiz`);

// Kurallarin eskimesi: yapi ayni, yapilandirma degisti.
const SONRA = [
  ["odunc dogrudan", "odunc>uyelik:b", "", "temiz"],
  ["kapiyi kaldir", "odunc>uyelik:s ucret>uyelik:s bildirim>uyelik:s",
    "uyelik-kapisi>uyelik odunc>uyelik-kapisi ucret>uyelik-kapisi bildirim>uyelik-kapisi", "temiz"],
  ["bildirim dogrudan", "bildirim>uyelik:b", "", "temiz"],
  ["onyuz uyelik", "sube-onyuz>uyelik:s", "", "temiz"],
  ["uyelik kimlige", "uyelik>kimlik:b", "", "ihlal"],
  ["rapor uyelik", "rapor>uyelik:s", "", "temiz"],
].map(([ad, e, s, gercek]) => ({ ad, ek: bag(e), sil: s ? bag(s) : [], gercek }));

const KAPALI = ["K1", "K5"];                                 // dis listesini okuyan iki kural
console.log("\nuyelik ice alindiktan sonra, ayni alti oneri (5 temiz, 1 ihlal)");
const S = [-22, 11, 8, 14, 13];
yaz(S, "yapilandirma", "yakalanan", "kacan", "yanlis alarm", "dogru gecen");
for (const [ad, y, k] of [["eski (degismemis)", ESKI, []], ["guncellenmis", YENI, []],
  [`eski, ${KAPALI.join("+")} kapali`, ESKI, KAPALI]]) {
  const r = kos(YAPI_B, SONRA, y, k);
  yaz(S, ad, r.yakalandi, r.kacti, r.yanlis, r.dogru);
}
const eK = kurallar(ESKI), yK = kurallar(YENI);
const degisen = Object.keys(ESKI).filter((a) => JSON.stringify(ESKI[a]) !== JSON.stringify(YENI[a]));
const davranis = Object.keys(eK).filter((x) =>
  SONRA.some((o) => eK[x](uygula(YAPI_B, o)) !== yK[x](uygula(YAPI_B, o))));
console.log(`guncelleme: ${Object.keys(ESKI).length} yapilandirma girdisinin ${degisen.length}'i ` +
  `(${degisen.join(", ")}); ${Object.keys(eK).length} kuralin ${davranis.length}'i ` +
  `(${davranis.join(", ")}) ayni oneride baska sonuc veriyor`);

const gevsek = kos(YAPI_B, ONERI, ESKI, KAPALI);
console.log(`${KAPALI.join("+")} kapatilinca ilk ihlal kumesi: yakalanan ${R.yakalandi} -> ` +
  `${gevsek.yakalandi}, kacan ${R.kacti} -> ${gevsek.kacti}, yanlis alarm ${R.yanlis} -> ${gevsek.yanlis}`);

// Cevrilemeyen kisim: yazilmamis degisikligin ucuzlugu.
const [SENARYO, GOZDEN, GELEN, KAPSANAN] = [6, 4, 12, 5];    // model girdisi
console.log(`\ndegisim senaryosu ${SENARYO}, yilda ${GOZDEN} gozden gecirme -> ` +
  `${SENARYO * GOZDEN} elle inceleme kalemi`);
console.log(`bir yilda gelen ${GELEN} degisiklikten ${KAPSANAN}'i yazili senaryoya denk dustu ` +
  `(%${((100 * KAPSANAN) / GELEN).toFixed(2)}); kalan ${GELEN - KAPSANAN} degisiklik gelmeden sayilmadi`);
```

```
uygunluk islevi                A       B
dugum                          9      14
bag                           16      20
dongudeki modul                2       0
yalitilmamis dis bag           9       0
tam yalitilmis dis           0/3     3/3

degisiklik                      A: dokunulan  A: geri alma  B: dokunulan  B: geri alma
yeni alan (katalog)                        6             6             1             1
bagimlilik degistir (uyelik)               6             6             1             1
ucret'i ikiye ayir                         6             6             3             3
toplam                                    18            18             5             5

B tabani: K1=0 K2=0 K3=0 K4=0 K5=0
oneri               gercek     atesleyen         sonuc
hizli arama          ihlal      K1,K4,K5     yakalandi
gece aktarimi        ihlal         K1,K5     yakalandi
borc sorgusu         ihlal         K2,K3     yakalandi
bildirimde ucret     ihlal            K3     yakalandi
kapidan katalog      ihlal            K5     yakalandi
yeni kiosk           ihlal            K3     yakalandi
bildirim kimlik      temiz             -   dogru gecti
rapor uyelik         temiz             -   dogru gecti
arsiv modulu         temiz             -   dogru gecti
kapi zinciri         ihlal             -         kacti
terminal uyelik      temiz            K4  yanlis alarm
bildirim uyelik      ihlal         K1,K5     yakalandi
yakalanan 7/8, kacan 1, yanlis alarm 1/4 temiz

uyelik ice alindiktan sonra, ayni alti oneri (5 temiz, 1 ihlal)
yapilandirma            yakalanan   kacan  yanlis alarm  dogru gecen
eski (degismemis)               1       0             5            0
guncellenmis                    1       0             0            5
eski, K1+K5 kapali              0       1             1            4
guncelleme: 4 yapilandirma girdisinin 3'i (dis, kapi, ucIzin); 5 kuralin 3'i (K1, K4, K5) ayni oneride baska sonuc veriyor
K1+K5 kapatilinca ilk ihlal kumesi: yakalanan 7 -> 4, kacan 1 -> 4, yanlis alarm 1 -> 1

degisim senaryosu 6, yilda 4 gozden gecirme -> 24 elle inceleme kalemi
bir yilda gelen 12 degisiklikten 5'i yazili senaryoya denk dustu (%41.67); kalan 7 degisiklik gelmeden sayilmadi
```

Sayılar **ölçüm** sınıfındadır; girdileri yukarıdaki varsayımlardır. A'da üç değişikliğin üçü de
altı iç modülün altısına dokunuyor: biçim bağı `odunc` ile `ucret` üzerinden sızdığı için katalog
kaydına alan eklemek bildirime kadar gidiyor. B'de aynı üç değişiklik 1, 1 ve 3 modüle dokunuyor —
18'e karşı 5. Geri alma sütunu ikinci ayrımı verir: A'da dokunulan her modül tek tek geri alınır,
B'de ilk iki değişiklik tek kapının içinde kaldığı için birer anahtar çevrilir. Bu yapısal bir geri
alınabilirliktir, Servis Mimarileri kursundaki boğucu incir geçişinin geçiş turu değil.
Yalıtımın bedeli de tabloda: B, A'nın 9 düğüm ve 16 bağına karşılık 14 ve 20 taşır. Üçüncü
değişikliğin B'de üç modüle dokunması yalıtımın kısmi olduğunu gösterir: `rapor` ücretlendirmeye
biçim bağıyla bağlı ve o bağ istisna listesinde.

#### Uygunluk İşlevi ve Bilinen İhlal Kümesi

**Uygunluk işlevi** (fitness function) burada değişime açıklığı ölçer ve üç nicelik sayar:
döngüdeki modül, yalıtılmamış dış bağ, tam yalıtılmış dış bağımlılık. A'da `odunc`
ile `ucret` karşılıklı bağlıdır, dokuz bağ dış bağımlılığa doğrudan gider ve hiçbir dış
bağımlılığın tek kapısı yoktur; B'de sırasıyla 0, 0 ve 3/3.

Beş **mimari kural** bu niceliklerin eşiğidir: K1 iç modülün dış bağımlılığa doğrudan bağlanmasını,
K2 döngüyü, K3 kapısı olan iç modüle biçim bağını, K4 uç katmanın izin listesi dışına çıkmasını, K5
bir dış bağımlılığın tam kapısı olmamasını yasaklar; beşi de B tabanında sıfır verir. **YN16 —
bilinen ihlal kümesi:** on iki öneri elle etiketlenmiştir, sekizi değişim maliyetini artırır.

Denetim sekiz ihlalin yedisini yakalar, birini kaçırır, dört temiz önerinin birine yanlış alarm
verir. Kaçan öneri **kapı zinciri**dir: kapının başka bir kapıya bağlanması. Kapı, biçim eşleyen
katman olmaktan çıkıp alan modülüne döner, ama hiçbir kural bunu yasaklamaz — denetim yalnız
yazılmış olanı görür. Yanlış alarm **terminal üyelik**tir; kusur önerinin değil K4'ün izin
listesinindir. Yakalama ile yanlış alarm, sınama kurslarındaki yanlış kalma ile yanlış geçme
ikilisinin aynı olgusudur; burada sınanan kod değil graftır.

#### Kuralın Kendisi Eskiyor

**YN17 — evrim olayı:** ayrı üyelik sistemi içeri alınır ve ağın kendi modülü olur. Grafta tek bir
kenar değişmez; değişen şey kural yapılandırmasının "dış" listesidir. Aynı beş kural, üyelik içeri
alındıktan sonra doğru olan altı öneriye uygulanır: beş temiz önerinin beşini de ihlal işaretler.
Kapıyı kaldırmak, ödüncün üyeliği doğrudan çağırması, raporun üyelikten okuması — hepsi K1 ile
K5'e takılır, çünkü ikisi de üyeliği hâlâ dışarıda sanır. Kural bozulmadı; dünyası değişti.

Düzeltme yapılandırmadadır: dört girdinin üçü (dış, kapı ve uç izin listeleri) güncellenir, beş
kuralın üçü aynı öneride başka sonuç verir. Sonrasında yanlış alarm sıfırlanır, tek gerçek ihlal —
içeri alınan üyeliğin kimlik servisini kapısız çağırması — yakalanmayı sürdürür.

İkinci yol gevşetmektir: dış listesini okuyan iki kuralı kapatmak; alt satır bedeli veriyor. Evrim
sonrası kümede yanlış alarm beşten bire iner ama tek gerçek ihlal kaçar; ilk ihlal kümesinde
yakalanan yediden dörde düşer, kaçan birden dörde çıkar, yanlış alarm birde kalır — kalan yanlış
alarm K4'ündü ve K4 açık kaldı. **Yanlış alarmın ekipte ürettiği davranış kuralı kapatmaktır;
kapatma da yanlış alarmı doğuran kuralı değil, en çok ihlal yakalayan kuralı götürür.**

#### Çevrilemeyen Kısım

Bir sayıya bağlanamayan şey **henüz istenmemiş değişikliğin ucuzluğu**dur: uygunluk işlevi yapıyı
yazılmış bir değişiklik kümesine karşı ölçer, kümede olmayan için ürettiği sayı yoktur. Yerine
konan şey dönem dönem gözden geçirilen **değişim senaryosu**dur. **YN18:** altı senaryo ve yılda
dört gözden geçirme, yılda 24 elle inceleme kalemi eder; bir yılda gelen on iki
değişikliğin beşi yazılı bir senaryoya denk düşer (yüzde 41,67), kalan yedisi ancak geldikten sonra
fiyatlanır. Bu oran geriye bakılarak sayılır; on üçüncü, yazılmamış değişikliğin bedeli önceden
sayılamaz.

#### Özet

- Aynı üç değişiklik A'da altı iç modülün altısına dokunuyor (18 dokunuş, 18 geri alma adımı);
  yalıtma noktalı B'de 1, 1 ve 3 modüle (5 ve 5).
- Uygunluk işlevi açıklığı üç nicelikle sayar (döngüdeki modül 2 → 0, yalıtılmamış dış bağ 9 → 0,
  tam yalıtılmış dış bağımlılık 0/3 → 3/3); yalıtımın kalıcı bedeli, A'nın 9 düğüm ve 16 bağına
  karşılık B'nin 14 düğümü ve 20 bağıdır.
- Denetim sekiz ihlalin yedisini yakalar, kapıdan kapıya bağı kaçırır, bir temiz öneriye yanlış
  alarm verir; ikisi de kuralın yazılışındandır.
- Bir bağımlılık içeri alınınca graf değişmeden kural eskir: beş doğru değişikliğin beşi ihlal
  işaretlenir; dört yapılandırma girdisinden üçü güncellenince yanlış alarm sıfırlanır, güncelleme
  yerine iki kural kapatılırsa yakalanan 7'den 4'e iner.
- Çevrilemeyen kısım henüz istenmemiş değişikliğin ucuzluğudur; yerine konan altı senaryo ve yılda
  dört gözden geçirme (24 kalem) gelen on iki değişikliğin beşini kapsar.

#### Sonraki Adım

Bu dersteki kuralların hepsi içeriden geldi: kapı, izin ve istisna listelerini mimarinin kendisi
seçti, eskidiklerinde yine kendisi güncelledi; gevşetmek de onun elindeydi. Bazı kısıtlar
ise dışarıdan gelir ve pazarlığa açık değildir — verinin nerede duracağı, ne kadar saklanacağı,
silme isteğinin nasıl karşılanacağı gibi. Sonraki ders bunları ele alır: kaldırılması seçenek
olmayan bir kısıt denetime çevrildiğinde ne oluyor, çevrilemeyen kısmın yerine ne konuyor?

### Uyumluluk Gereksinimleri

Source: https://academia.sh/tr/kurslar/mimari-yonetisim/uyumluluk-gereksinimleri

Dışarıdan gelen kısıtların denetime çevrilmesi: beş kısıt türünün 15 bildirilmiş kopya üzerinde koşturulması, dar erişim tanımıyla 10 ihlalin 7'sinin yakalanıp 3'ünün kaçması, tanım genişleyince yakalananın 8'e ve yanlış alarmın 3'e çıkması, gevşetme yerine yılda 27.375 satır kanıt üretilmesi ve kısıtın mimariye bir yeni bileşen ile 11 yeni bağa mal olması.

Önceki ders kuralın eskimesini ölçtü ve iki çıkış yolu saydı: yapılandırmayı güncellemek ya da
kuralı gevşetmek. İkisi de mimarinin elindeydi, çünkü kuralları mimarinin kendisi koymuştu — kapı
listesini, izin listesini ve istisnaları seçen de, eskiyince değiştiren de aynı taraftı.

Bu dersin kısıtları dışarıdan gelir. Bir **uyumluluk gereksinimi** (compliance requirement),
sistemin dışındaki bir yükümlülüğün mimariye düşen payıdır; kaldırmak, gevşetmek ya da kendi
kararıyla istisna yazmak seçenek değildir. Kursun ölçüsü aynı kalır — kısıt koşturulabilir bir
denetime çevrilebiliyor mu, çevrilemiyorsa yerine ne konuyor — ama üç yerde ayrışır: gevşetmenin
yerini **kanıt** alır, çevrilemeyen kısım kuralın yorumunda durur ve kısıt mimarinin biçimini
doğrudan değiştirir. Kısıtlar burada **tür olarak** anılır: saklama süresi zorunluluğu, veri
yerleşimi kısıtı, silme hakkı, erişim kaydı tutma yükümlülüğü, denetlenebilirlik yükümlülüğü.

#### Beş Kısıt Türü, Beş Denetim

Örnek yine kursun kurgu **bölgesel kütüphane ağı**dır. **YN19 — veri sınıfı çizelgesi:** beş veri
sınıfı, her biri için saklama alt sınırı, üst sınırı, izinli bölge, silme hakkının olup olmadığı ve
erişim kaydı yükümlülüğü. **YN20 — bildirilmiş kopya çizelgesi:** on beş kopya; her satır bir veri
sınıfının bir bileşende, bir bölgede duran örneğidir ve en eski kaydın yaşını, erişim kaydının
tutulup tutulmadığını, silme akışına bağlı olup olmadığını taşır. **YN21 — bilinen ihlal kümesi:**
on elle etiketli ihlal; ikisi çizelgede hiç görünmeyen **bildirilmemiş kopya**dır. **YN22 — erişim
tanımının iki kapsamı:** dar tanım yalnız doğrudan okumayı erişim sayar, geniş tanım türetilmiş
okumayı da sayar.

Aşağıdaki dosya çizelgeleri, beş denetimi ve ölçümü kurar. Süreç içi bir **modeldir**; gerçek depo,
ağ ya da kayıt yoktur.

```js
// uyum/olc.mjs — disaridan gelen kisitlarin koşturulabilir denetime cevrilmesi. Surec ici model:
// bolgesel kutuphane agi (kurgu); gercek depo, ag ve kayit yoktur, her kopya bir satirdir.

const satir = (s) => s.trim().split("\n").map((l) => l.trim().split("|"));
const yaz = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));

// Veri sinifi: ad|saklama alt siniri (gun)|ust sinir (0 = yok)|izinli bolge|silme hakki|erisim kaydi
const SINIF = Object.fromEntries(satir(`
  uye-kimlik|0|1825|ic|1|1
  odunc-kaydi|365|1095|ic|1|1
  odeme-kaydi|3650|0|ic|0|1
  arama-gecmisi|0|180|ic dis|1|1
  erisim-kaydi|730|2555|ic|0|0`).map(([ad, alt, ust, b, s, k]) => [ad, { alt: +alt, ust: +ust,
  bolge: b.split(" "), silme: s === "1", kayit: k === "1" }]));

// Bildirilmis kopya: sinif|bilesen|bolge|en eski kaydin yasi|erisim kaydi tutuluyor|silme akisinda|okuma
const KOPYA = satir(`
  uye-kimlik|uyelik|ic|900|1|1|dogrudan
  uye-kimlik|odunc|ic|1200|1|1|dogrudan
  uye-kimlik|bildirim|ic|400|0|0|dogrudan
  uye-kimlik|arsiv|dis|2000|0|0|dogrudan
  uye-kimlik|yedek|ic|2200|0|0|dogrudan
  odunc-kaydi|odunc|ic|1200|1|1|dogrudan
  odunc-kaydi|rapor|ic|1500|0|0|turetilmis
  odunc-kaydi|arsiv|dis|2000|0|0|dogrudan
  odeme-kaydi|ucret|ic|2000|1|0|dogrudan
  odeme-kaydi|yedek|ic|2200|0|0|dogrudan
  odeme-kaydi|rapor|ic|3700|0|0|turetilmis
  arama-gecmisi|katalog-kapisi|ic|30|0|1|turetilmis
  arama-gecmisi|rapor|ic|300|0|0|turetilmis
  erisim-kaydi|olay-gunlugu|ic|800|1|0|dogrudan
  erisim-kaydi|yedek|ic|2200|0|0|dogrudan`).map(([sinif, bilesen, bolge, yas, kayit, silme, okuma]) =>
  ({ ad: `${sinif}|${bilesen}`, sinif, bilesen, bolge, yas: +yas,
    kayit: kayit === "1", silme: silme === "1", okuma }));

// Bes kisit turu. "kapsam" erisim tanimidir: dar yalnizca dogrudan okumayi erisim sayar.
const denetim = (kapsam) => ({
  "D1 saklama ust siniri": (k) => SINIF[k.sinif].ust > 0 && k.yas > SINIF[k.sinif].ust,
  "D2 saklama alt siniri": (k) => SINIF[k.sinif].alt > 0 && k.yas < SINIF[k.sinif].alt,
  "D3 veri yerlesimi": (k) => !SINIF[k.sinif].bolge.includes(k.bolge),
  "D4 silme hakki": (k) => SINIF[k.sinif].silme && !k.silme,
  "D5 erisim kaydi": (k) => SINIF[k.sinif].kayit && !k.kayit
    && (kapsam === "genis" || k.okuma === "dogrudan"),
});

// Elle etiketlenmis gercek ihlal kumesi (model girdisi); son ikisi bildirilmemis kopyadir
const GERCEK = satir(`
  uye-kimlik|bildirim
  uye-kimlik|arsiv
  odunc-kaydi|odunc
  odunc-kaydi|rapor
  odunc-kaydi|arsiv
  odeme-kaydi|yedek
  arama-gecmisi|katalog-kapisi
  arama-gecmisi|rapor
  uye-kimlik|rapor
  odeme-kaydi|bildirim`).map((x) => x.join("|"));

const KAPSAM = ["dar", "genis"];
console.log(`${KOPYA.length} bildirilmis kopya, ${Object.keys(SINIF).length} veri sinifi, ` +
  `${Object.keys(denetim("dar")).length} kisit turu, ${GERCEK.length} elle etiketli ihlal ` +
  `(${GERCEK.filter((a) => !KOPYA.some((k) => k.ad === a)).length} tanesi bildirilmemis kopya)`);

const K = [-24, 12, 12];
console.log();
yaz(K, "kisit turu", "isaretli: dar", "genis");
for (const ad of Object.keys(denetim("dar")))
  yaz(K, ad, ...KAPSAM.map((s) => KOPYA.filter(denetim(s)[ad]).length));

const S = [-16, 16, 11, 7, 14, 13], R = {};
console.log();
yaz(S, "erisim tanimi", "isaretli kopya", "yakalanan", "kacan", "yanlis alarm", "dogru gecen");
for (const s of KAPSAM) {
  const d = denetim(s);
  const isaretli = KOPYA.filter((k) => Object.values(d).some((f) => f(k)));
  const yakalanan = isaretli.filter((k) => GERCEK.includes(k.ad)).length;
  R[s] = { isaretli: isaretli.length, yakalanan, kacan: GERCEK.length - yakalanan,
    yanlis: isaretli.length - yakalanan,
    dogru: KOPYA.length - isaretli.length - (GERCEK.length - yakalanan
      - GERCEK.filter((a) => !KOPYA.some((k) => k.ad === a)).length) };
  yaz(S, s, R[s].isaretli, `${yakalanan}/${GERCEK.length}`, R[s].kacan, R[s].yanlis, R[s].dogru);
}

// Kanit: gevsetme secenegi yok, kosum kaydi isteniyor. Kanit da bir veri sinifidir.
const SATIR_BAYT = 120, GUN = 365, SAKLA = SINIF["erisim-kaydi"].ust;
const kosum = KOPYA.length * Object.keys(denetim("dar")).length;
console.log(`\nkanit: kosum basina ${KOPYA.length} x 5 = ${kosum} satir; gunde bir kosum -> ` +
  `yilda ${(kosum * GUN).toLocaleString("tr-TR")} satir, ` +
  `${((kosum * GUN * SATIR_BAYT) / 1e6).toFixed(2)} MB`);
console.log(`kanit ${SAKLA} gun saklanir (erisim kaydi ust siniri) -> ` +
  `${(kosum * SAKLA).toLocaleString("tr-TR")} satir, ${((kosum * SAKLA * SATIR_BAYT) / 1e6).toFixed(2)} MB`);

// Kisitin mimariye maliyeti: yerinden oynayan bilesen ve yeni bag
const M = [-28, 10, 12];
console.log();
yaz(M, "mimariye maliyet", "dar", "genis");
const disari = new Set(KOPYA.filter(denetim("dar")["D3 veri yerlesimi"]).map((k) => k.bilesen));
const silmeBag = KOPYA.filter(denetim("dar")["D4 silme hakki"]).length;
yaz(M, "yerinden oynayan bilesen", disari.size, disari.size);
yaz(M, "yeni bilesen (silme yayini)", 1, 1);
yaz(M, "yeni bag: silme akisi", silmeBag, silmeBag);
const kayitBag = KAPSAM.map((s) => KOPYA.filter(denetim(s)["D5 erisim kaydi"]).length);
yaz(M, "yeni bag: erisim kaydi", ...kayitBag);
yaz(M, "yeni bag toplam", ...kayitBag.map((x) => x + silmeBag));

// Cevrilemeyen kisim: yukumlulugun yorumu ve kapsamin siniri
const [YORUM, GOZDEN, SAAT] = [6, 2, 3];                     // model girdisi
console.log(`\ncevrilemeyen: yukumlulugun yorumu ve kapsamin siniri; yerine ${YORUM} yorum kaydi, ` +
  `yilda ${GOZDEN} gozden gecirme x ${SAAT} kisi-saat = ${YORUM * GOZDEN * SAAT} kisi-saat/yil`);
console.log(`yorum dar'dan genis'e cevrilince: yakalanan ${R.dar.yakalanan} -> ${R.genis.yakalanan}, ` +
  `kacan ${R.dar.kacan} -> ${R.genis.kacan}, yanlis alarm ${R.dar.yanlis} -> ${R.genis.yanlis}, ` +
  `yeni bag ${kayitBag[0] + silmeBag} -> ${kayitBag[1] + silmeBag}`);
```

```
15 bildirilmis kopya, 5 veri sinifi, 5 kisit turu, 10 elle etiketli ihlal (2 tanesi bildirilmemis kopya)

kisit turu              isaretli: dar       genis
D1 saklama ust siniri              6           6
D2 saklama alt siniri              2           2
D3 veri yerlesimi                  2           2
D4 silme hakki                     6           6
D5 erisim kaydi                    5           9

erisim tanimi     isaretli kopya  yakalanan  kacan  yanlis alarm  dogru gecen
dar                            9       7/10      3             2            5
genis                         11       8/10      2             3            4

kanit: kosum basina 15 x 5 = 75 satir; gunde bir kosum -> yilda 27.375 satir, 3.29 MB
kanit 2555 gun saklanir (erisim kaydi ust siniri) -> 191.625 satir, 23.00 MB

mimariye maliyet                   dar       genis
yerinden oynayan bilesen             1           1
yeni bilesen (silme yayini)          1           1
yeni bag: silme akisi                6           6
yeni bag: erisim kaydi               5           9
yeni bag toplam                     11          15

cevrilemeyen: yukumlulugun yorumu ve kapsamin siniri; yerine 6 yorum kaydi, yilda 2 gozden gecirme x 3 kisi-saat = 36 kisi-saat/yil
yorum dar'dan genis'e cevrilince: yakalanan 7 -> 8, kacan 3 -> 2, yanlis alarm 2 -> 3, yeni bag 11 -> 15
```

Sayılar **ölçüm** sınıfındadır; girdileri yukarıdaki varsayımlardır. Beş kısıt türünün beşi de
koşturulabilir bir denetime çevriliyor: saklama süresinin iki ucu birer karşılaştırma, yerleşim
bir küme üyeliği, silme hakkı bir erişilebilirlik sorusu, erişim kaydı bir yüklem. Dar tanımda on
beş kopyanın dokuzu işaretleniyor; on ihlalin yedisi yakalanıyor, üçü kaçıyor, iki temiz kopyaya
yanlış alarm veriliyor.

Kaçan üçün ikisi denetimin göremeyeceği yerdedir: çizelgede bildirilmemiş kopyalar. Denetim yalnız
bildirilen kopyayı okur ve bildirilmeyen için ürettiği sayı yoktur — bu kuralın kaçırması değil,
**veri kaynağının eksikliğidir** ve kuralı sıkılaştırmak onu kapatmaz. Yanlış alarmların ikisi de
kapsam kusurudur: yedekteki üye kimliği saklama üst sınırını aştığı için işaretleniyor ama yedek
ayrı bir düzenin konusudur; ücret bileşenindeki ödeme kaydı alt sınırın altında görünüyor ama alt
sınırı karşılayan kayıt başka bir depoda duruyor. İkisi de denetimin yanlışı değil, denetimin
baktığı çizelgenin dar oluşudur.

#### Gevşetme Yok, Kanıt Var

Önceki derste yanlış alarmın ürettiği davranış kuralı kapatmaktı ve iki kural kapatıldığında
yakalanan yediden dörde inmişti. Burada o düğme yoktur. Dışarıdan gelen kısıtın ayırt edici yanı,
uyulmadığının değil **uyulduğunun gösterilmesinin** istenmesidir: istisna yerine kanıt.

Kanıt bir çıktıdır ve bedeli sayılır. Koşum başına on beş kopya çarpı beş kısıt, yani 75 satır;
günde bir koşumla yılda 27.375 satır ve 3,29 MB. Kanıt kaydı denetlenebilirlik yükümlülüğü gereği
saklanır — **YN23**, erişim kaydının üst sınırı olan 2555 gün — ve toplamı 191.625 satır, 23,00 MB
olur. Buradaki kapalı halka söylenmeye değer: **kanıt da bir veri sınıfıdır.** Kendi saklama süresi,
kendi yerleşim kısıtı ve kendi erişim kaydı yükümlülüğü vardır; denetim, ürettiği kaydı da
denetlemek zorundadır.

#### Kısıtın Mimariye Maliyeti

Kısıt yalnız bir denetim eklemez, bileşenleri de yerinden oynatır. **YN24 — kısıtın uygulanışı:**
izinli bölgenin dışında duran kopyaları taşıyan bileşen bölge içine taşınır, silme hakkı için bir
**silme yayını** bileşeni eklenir ve akışa bağlı olmayan her kopyaya bir bağ açar, erişim kaydı
tutulmayan her erişim için olay günlüğüne bir bağ açılır.

Alt tablo bunu sayıyor: bir bileşen yerinden oynuyor (arşiv, bölge dışındaki iki kopyayı taşıdığı
için), bir bileşen doğuyor ve on bir yeni bağ açılıyor — altısı silme akışı, beşi erişim kaydı. Bu
bağların hiçbiri bir niteliği yükseltmek için seçilmedi ve hiçbiri bir ödünleşmenin sonucu değil;
mimarinin biçimini burada bir dış yükümlülük belirliyor. Ölçünün değeri de bu: kısıtın bedeli
"uyum maliyeti" diye tek kalemde değil, taşınan bileşen ve açılan bağ olarak duruyor.

#### Çevrilemeyen Kısım: Yükümlülüğün Yorumu

Beş denetimin beşi de bir tanımı verilmiş kabul eder. "Erişim" nedir — bir raporun türetilmiş
okuması erişim midir? "Silme" nedir — maskeleme silme sayılır mı? Denetim bu soruları yanıtlamaz,
yanıtı **girdi olarak alır**. Çevrilemeyen kısım budur: yükümlülüğün yorumu ve kapsamın sınırı.

Yerine konan şey yazılı yorum kaydıdır ve bedeli sayılır — **YN25:** altı yorum kaydı, yılda iki
gözden geçirme, kalem başına üç kişi-saat, toplam 36 kişi-saat. Asıl ölçüm ise yorumun sonucudur.
Aynı denetim, "erişim" tanımı dardan genişe çevrildiğinde başka bir şey ölçer: yakalanan yediden
sekize çıkar, kaçan üçten ikiye iner, yanlış alarm ikiden üçe çıkar ve mimarideki yeni bağ on
birden on beşe. Tek bir sözcüğün kapsamı dört sayıyı birden değiştiriyor. **Çevrilemeyen kısım
denetimin dışında değil, girdisindedir**; kuralın kendisi kadar açık yazılmadığında denetimin
ürettiği her sayı tartışmalı hâle gelir.

#### Özet

- Beş dış kısıt türünün beşi de koşturulabilir denetime çevrildi: saklama süresinin alt ve üst
  sınırı, veri yerleşimi, silme hakkı, erişim kaydı.
- Dar erişim tanımında on beş bildirilmiş kopyanın dokuzu işaretleniyor; on ihlalin yedisi
  yakalanıyor, üçü kaçıyor, iki temiz kopyaya yanlış alarm veriliyor.
- Kaçan üçün ikisi bildirilmemiş kopyadır: denetim yalnız bildirilen kopyayı okur ve bildirilmeyen
  için ürettiği sayı yoktur.
- Gevşetme seçeneği yoktur, yerine kanıt istenir: yılda 27.375 satır ve 3,29 MB, 2555 günlük
  saklamayla 191.625 satır ve 23,00 MB. Kanıt da bir veri sınıfıdır ve kendi kısıtlarına tabidir.
- Kısıtın mimariye maliyeti sayıldı: bir bileşen yerinden oynuyor, bir bileşen doğuyor, on bir yeni
  bağ açılıyor (geniş tanımda on beş).
- Çevrilemeyen kısım yükümlülüğün yorumu ve kapsamın sınırıdır; yerine konan altı yorum kaydı yılda
  36 kişi-saat eder ve tanımı genişletmek yakalananı 7'den 8'e, yanlış alarmı 2'den 3'e taşır.

#### Kurs Kapanışı

Kurs on derste tek bir soruyu sordu: bir nitelik ya da mimari kural, koşturulabilir bir denetime
çevrilebiliyor mu, çevrilemiyorsa yerine ne konuyor?

| Ders | Denetime çevrilen | Yakalanan / kaçan / yanlış alarm | Çevrilemeyen ve yerine konan |
|---|---|---|---|
| Nitelik Ağacı | 30 yaprağın 25'i bir sayıya, 20'si denetime bağlandı | 11 / 7 / 23 | 10 yaprak; çeyrek ölçüm oturumu ve gözden geçirme kaçanın 4'ünü 8,5 pencere gecikme ve 100 kişi-saatle yakalıyor |
| Nitelikler Arası Gerilim | 12 karardan gerilim matrisi, eşik taraması | sekiz puanda 4 / 1 / 0, altı puanda 4 / 1 / 5 | düşüşün kabul edilebilirliği bir yargıdır; yerine 5 muafiyet kaydı, yanlış alarm 5 → 0 |
| Güvenlik Mimarisi | 7 bölge, 8 sınır, 18 geçiş; sınır başına gerekli denetim | 6 / 4 / 3 (3 açık boşluk) | sınırın doğru yerde olup olmadığı; 8 sınır için çeyrekte gözden geçirme, 24 kişi-saat |
| Ölçeklenebilirlik Kararları | üç ölçek birimi adayında kopyalama kuralı | 5 / 1 / 1 (3 doğru sessizlik) | bildirilmeyen paylaşılan kaynak; yerine kopyalama sınaması, 10 denemede 60 kişi-saat |
| Sürdürülebilirlik Ölçütleri | 12 değişikliğin dosya, modül ve derinlik ölçütü; eşik kuralı | 3 / 1 / 1 (7 doğru sessizlik) | değişikliğin zorluğu; çeyrekte 12 kişi-saatlik yerleşim gözden geçirmesi, altı çeyrekte 72 |
| Mimari Yönetişim Modelleri | 9 yönetişim kuralının 5'i denetime çevrildi; 24 karar | merkezî 12 / 0 / 8, federatif 9 / 3 / 1, danışman 6 / 6 / 1 | 4 kural; yerine 1/3 örneklem, 32 elle bakış, 12 ihlalin 7'sini görmüyor |
| Uygunluk İşlevleri | F1 yüzey büyüklüğü, F2 ölü yüzey oranı | F1 `> 2` ile 5 / 0 / 6, `> 10` ile 1 / 4 / 0; F2 4 / 1 / 1 | "dışa açılan ad modülün işini anlatır"; yerine 66 sembolün 22'sini okuyan örneklem |
| Bağımlılık ve Katman Denetimi | aynı katman kuralının beş biçimi; 13 modül, 34 kenar | yasak listesi 3 / 6 / 0, katman izin listesi 7 / 2 / 1, modül izin listesi 9 / 0 / 0 | onaylanmış göç kenarı kuralla anlatılamıyor; yerine 2 süreli istisna kaydı, süre denetimi olmadan yakalama 7 → 6 |
| Evrimsel Mimari | değişime açıklığı ölçen uygunluk işlevi; 12 öneri | 7 / 1 / 1 | henüz istenmemiş değişikliğin ucuzluğu; yerine 6 senaryo ve yılda 4 gözden geçirme (24 kalem), gelen 12 değişikliğin 5'ini kapsıyor |
| Uyumluluk Gereksinimleri | beş dış kısıt türü, 15 bildirilmiş kopya | dar tanımda 7 / 3 / 2, geniş tanımda 8 / 2 / 3 | yükümlülüğün yorumu ve kapsamın sınırı; yerine 6 yorum kaydı, yılda 36 kişi-saat |

Tablonun kuralı adıyla anılmalı: **bir nitelik ya da mimari kural, koşturulabilir bir denetime
çevrilebildiği ölçüde yönetilebilir; çevrilemeyen kısım adıyla yazılır ve yerine konan sayılır.**
"Bu nitelik önemlidir" cümlesi bu kursta hiçbir yerde karar taşımadı. Üç sütun birbirinin yerine de
geçmez: ikincisi denetimin neye baktığını, üçüncüsü ne kadarını doğru gördüğünü, dördüncüsü hiç
bakamadığı yerde ne yapıldığını söyler. Üçünü birden yazmayan bir yönetişim düzeni yalnız
yakaladıklarını bilir.

Tabloda iki örüntü tekrar ediyor. Birincisi, yakalamayı artıran her ayar yanlış alarmı da
artırıyor — Nitelik Ağacı'nın 23 alarmı, Nitelikler Arası Gerilim'in altı puanlık eşiği, bu dersin
geniş erişim tanımı — ve yanlış alarmın bedeli kuralın kapatılmasıdır: Nitelik Ağacı'nda iki
denetim kapatılınca yakalanan 11'den 9'a, Uygunluk İşlevleri'nde F1 dördüncü yayımda kapanınca 9
ihlalli yayımın 8'i, Evrimsel Mimari'de iki kural kapatılınca yakalanan 7'den 4'e gitti. İkincisi,
çevrilemeyenin yerine konan şey her derste **insan zamanıdır** ve pahalıdır: 100, 24, 60, 72 ve 36
kişi-saat, 32 elle bakış, 22 sembollük örneklem, 24 inceleme kalemi.

Kursun bıraktığı soru tablonun altındadır. Kurallar yazıldı, denetime çevrildi, yakaladığı ve
kaçırdığı sayıldı. Ama kuralı yazan, koşturan, yanlış alarm karşısında kapatan ve çevrilemeyen
kısım için gözden geçirme oturumuna oturan **insanlardır**. Ekiplerin nasıl bölündüğü, kararın
hangi sınırda alındığı ve teslimatın nasıl aktığı mimariyi doğrudan biçimlendiriyor; bu kurs
boyunca hiç ele alınmadı. Sonraki kurs **Süreç, Ekip ve Teslimat** o katmanı ele alır.

