---
title: 'Temiz Mimari'
source: 'https://academia.sh/tr/kurslar/mimari-bicemler/temiz-mimari'
course: 'Mimari Biçemler'
language: tr
updated: '2026-08-17T18:10:36+00:00'
license: 'CC BY-SA 4.0'
---

# Temiz Mimari

İki halka kuralının tek bir yükleme birleştirilmesi: on altı halka kenarından yedisini yasaklayan kuralın yazılması, bileşim kökünden geçen sınırlarda denetim akışının yönü ile bağımlılık okunun ayrıldığı kenarların sayılması ve sınırdan geçen iki veri biçiminin erişilebilir ad ile karakter sayısıyla karşılaştırılması.

Bir önceki ders iki halka kuralını aynı dört bölmeye uyguladı ve her birinin ötekinin yakaladığı
kusura kör olduğunu ölçtü. Birleşik yasak 7 kenardı, ama hiçbir düzen o yediyi birlikte
yasaklamıyordu: biri içerinin sırasını ilan ediyor ama dış kuşağı tek parça sayıyordu, öteki dış
kuşağı sıralamıyordu.

Bu ders o birleşimi adlandırılmış bir düzen olarak kurar. **Temiz mimari** (clean architecture)
halkaların sıralı olduğunu ve bağımlılık okunun daima içe baktığını birlikte söyler; yeni olan,
kuralın bütün halka çiftleri üzerinde tek bir yüklem olarak yazılmasıdır. İki ölçü bu yazımdan
çıkar: sınırdan geçen verinin alan ve karakter sayısı ile denetim akışı ve bağımlılık okunun
ayrıldığı kenar sayısı.

## Birleşmiş Kural

Politika ile ayrıntının ayrılması ve sınırın nereye çizileceği Tasarım İlkeleri kursundaki
Politika ve Ayrıntı Ayrımı ile Sınırların Çizilmesi derslerinde, katman sorumlulukları da Veri
Erişim Katmanı ve İş Mantığı kursundaki Katman Sorumlulukları dersinde kurulmuştu. Burada
anlatılan ilke değil **düzen**.

Halka 0'da **varlık nesneleri** ile kural durur ve dışta hiçbir şeye bakmaz. Halka 1'de
**kullanım senaryoları**: bir isteğin adımları, sırası ve senaryonun dışarıdan beklediği adların
bildirimi. Halka 2 bağdaştırıcılardır; dış biçimi girdi modeline, çıktı modelini dış biçime
çevirir. Halka 3 en dıştadır: ham satırlar ve giriş masası. Kural iki koşulun çarpımıdır —
soğandan geleni, bir halkanın yalnız kendinden içteki ya da kendi halkasını ithal edebilmesi;
altıgenden geleni, en dış halkaya hiçbir kenarın girememesi.

## Dört Halkaya Yerleşmiş Kitaplık

Ücretlendirme bağlamı dört dizine dağıtılıyor; dizin adı halka numarasını verir. Bağlar
parametreyle kuruluyor ve bileşim kökü halkaların dışındadır.

```sh
mkdir -p temiz/varliklar temiz/senaryolar temiz/bagdastiricilar temiz/altyapi
```

```js
// temiz/varliklar/gonderi.mjs — halka 0: fiyatlanabilir gonderi ve ucretlenen agirligi
const HACIM_BOLENI = 5000;

export const gonderi = (girdi) => Object.freeze({
  ...girdi,
  ucretlenenAgirlik: () => Math.max(girdi.agirlik, girdi.hacim / HACIM_BOLENI),
});
```

```js
// temiz/varliklar/ucret-kurali.mjs — halka 0: kural; kademe, katsayi ve oran disaridan gelir
const ASGARI_UCRET = 3990;
const YUVARLAMA_ADIMI = 50;
const SIGORTA_ORANI = 0.004;

export const ucretKurali = {
  hesapla: (g, kademeler, katsayi, indirimOrani) => {
    const agirlik = g.ucretlenenAgirlik();
    const kademe = kademeler.find((k) => agirlik <= k.ustAgirlik);
    if (kademe === undefined) throw new RangeError("agirlik kademesi yok");
    const ham = kademe.ucret * katsayi + Math.round(g.beyanDegeri * SIGORTA_ORANI);
    const net = Math.round((ham * (1 - indirimOrani)) / YUVARLAMA_ADIMI) * YUVARLAMA_ADIMI;
    return { tutar: Math.max(net, ASGARI_UCRET), kademe, ucretlenenAgirlik: agirlik };
  },
};
```

Halka 1'in ilk dosyası kod değil bildirimdir: girdi ile çıktı modelinin alanları ve senaryonun
geçitten beklediği adlar.

```js
// temiz/senaryolar/sinir.mjs — halka 1: girdi modeli, cikti modeli ve gecidin bekledigi adlar
export const GIRDI_ALANLARI = ["agirlik", "hacim", "bolge", "beyanDegeri", "sozlesme"];
export const CIKTI_ALANLARI = ["tutar", "paraBirimi", "kademeUstAgirlik", "ucretlenenAgirlik"];
export const GECIT_ADLARI = ["kademeler", "katsayi", "indirimOrani"];

export const eksikAd = (nesne, adlar) => adlar.filter((a) => typeof nesne?.[a] !== "function");

export const bildirilmemisAd = (nesne, izinli, onek = "") =>
  Object.entries(nesne ?? {}).flatMap(([ad, deger]) => {
    const yol = onek === "" ? ad : `${onek}.${ad}`;
    if (deger !== null && typeof deger === "object") return bildirilmemisAd(deger, izinli, yol);
    return izinli.includes(yol) ? [] : [yol];
  });
```

```js
// temiz/senaryolar/teklif-senaryosu.mjs — halka 1: girdi modelinden cikti modeline
import { gonderi } from "../varliklar/gonderi.mjs";
import { ucretKurali } from "../varliklar/ucret-kurali.mjs";
import { GECIT_ADLARI, eksikAd } from "./sinir.mjs";

export const teklifSenaryosu = (gecit) => {
  const eksik = eksikAd(gecit, GECIT_ADLARI);
  if (eksik.length > 0) throw new TypeError(`karsilanmayan gecit adi: ${eksik.join(", ")}`);
  return {
    calistir: (girdi) => {
      const g = gonderi(girdi);
      const sonuc = ucretKurali.hesapla(g, gecit.kademeler(), gecit.katsayi(g.bolge),
        gecit.indirimOrani(g.sozlesme));
      return {
        tutar: sonuc.tutar,
        paraBirimi: "kurus",
        kademeUstAgirlik: sonuc.kademe.ustAgirlik,
        ucretlenenAgirlik: sonuc.ucretlenenAgirlik,
      };
    },
  };
};
```

```js
// temiz/bagdastiricilar/teklif-denetleyicisi.mjs — halka 2: dis govdeyi girdi modeline, cikti modelini metne cevirir
import { CIKTI_ALANLARI, GIRDI_ALANLARI, bildirilmemisAd } from "../senaryolar/sinir.mjs";

const DONUSTUR = { agirlik: Number, hacim: Number, bolge: String, beyanDegeri: Number, sozlesme: String };

export const teklifDenetleyicisi = (senaryo) => ({
  teklifIstegi: (govde) => {
    const cikti = senaryo.calistir(
      Object.fromEntries(GIRDI_ALANLARI.map((a) => [a, DONUSTUR[a](govde[a])])));
    const bildirilmemis = bildirilmemisAd(cikti, CIKTI_ALANLARI);
    if (bildirilmemis.length > 0) return { durum: 500, govde: `sozlesme disi ad: ${bildirilmemis.join(", ")}` };
    return { durum: 200, govde: `${(cikti.tutar / 100).toFixed(2)} TL / ${cikti.ucretlenenAgirlik.toFixed(2)} kg` };
  },
});

export const hataBicimleyicisi = (hata) =>
  hata instanceof RangeError ? { durum: 422, govde: hata.message } : { durum: 500, govde: "beklenmeyen" };
```

```js
// temiz/bagdastiricilar/tarife-gecidi.mjs — halka 2: ham satirlari kademe degerine cevirir
import { GECIT_ADLARI, eksikAd } from "../senaryolar/sinir.mjs";

export const KAYNAK_ADLARI = ["satirlar", "katsayilar", "sozlesmeler"];

export const tarifeGecidi = (kaynak) => {
  const gecit = {
    kademeler: () => kaynak.satirlar()
      .map(([ustAgirlik, tl]) => ({ ustAgirlik, ucret: Math.round(tl * 100) })),
    katsayi: (bolge) => kaynak.katsayilar()[bolge] ?? 1.8,
    indirimOrani: (sozlesme) => kaynak.sozlesmeler()[sozlesme] ?? 0,
  };
  const eksik = [...eksikAd(kaynak, KAYNAK_ADLARI), ...eksikAd(gecit, GECIT_ADLARI)];
  if (eksik.length > 0) throw new TypeError(`karsilanmayan ad: ${eksik.join(", ")}`);
  return gecit;
};
```

```js
// temiz/altyapi/tarife-satirlari.mjs — halka 3: ham satirlar, bolge katsayilari, sozlesme oranlari
const SATIRLAR = [[1, 49.9], [5, 84.9], [15, 149.9], [30, 249.9]];
const KATSAYILAR = { yakin: 1, orta: 1.35, uzak: 1.8 };
const SOZLESMELER = { yok: 0, standart: 0.05, hacimli: 0.12 };

export const tarifeSatirlari = {
  satirlar: () => SATIRLAR,
  katsayilar: () => KATSAYILAR,
  sozlesmeler: () => SOZLESMELER,
};
```

```js
// temiz/altyapi/masa.mjs — halka 3: disaridan geleni denetleyiciye verir, hatayi bicimleyiciden gecirir
import { hataBicimleyicisi } from "../bagdastiricilar/teklif-denetleyicisi.mjs";

export const masa = (denetleyici) => ({
  istek: (govde) => {
    try { return denetleyici.teklifIstegi(govde); } catch (hata) { return hataBicimleyicisi(hata); }
  },
});
```

## Kuralın Yazımı ve İhlal

Araç kuralı iki kuraldan üretir, birleşimle aynı olduğunu sınar, sonra ağacın halka kenarlarını
ithal satırlarından okuyup yasağa karşı sayar. Kenar çıkarma yordamı Tasarım İlkeleri kursundaki
Bağımlılık Çizgesi Sağlığı dersinde kurulmuştu.

```js
// temiz-kural.mjs — temiz kurali iki kuralin birlesimi olarak kurar ve agactaki ihlali sayar
import { readdirSync, readFileSync } from "node:fs";
import { basename, dirname, join, normalize } from "node:path";

const HALKA = { varliklar: 0, senaryolar: 1, bagdastiricilar: 2, altyapi: 3 };
const ALTIGEN = (i, j) => j !== 3;
const SOGAN = (i, j) => j <= i;
const TEMIZ = (i, j) => SOGAN(i, j) && ALTIGEN(i, j);

const tum = [0, 1, 2, 3].flatMap((i) => [0, 1, 2, 3].map((j) => [i, j]));
const yasakla = (izin) => new Set(tum
  .filter(([i, j]) => izin(i, j) === false).map(([i, j]) => `${i}->${j}`));
export const YASAK = yasakla(TEMIZ);

export function halkaKenarlari(kok) {
  const kenar = new Map();
  for (const halka of readdirSync(kok).sort()) {
    for (const dosya of readdirSync(join(kok, halka)).sort()) {
      for (const m of readFileSync(join(kok, halka, dosya), "utf8")
        .matchAll(/^import\s.*?from\s+"(\.[^"]+)"/gm)) {
        const hedef = basename(dirname(normalize(join(kok, halka, m[1]))));
        kenar.set(`${HALKA[halka]}->${HALKA[hedef]}`, `${halka}/${dosya}`);
      }
    }
  }
  return kenar;
}

if (process.argv.length > 2) {
  const birlesim = new Set([...yasakla(ALTIGEN), ...yasakla(SOGAN)]);
  console.log(`olasi halka kenari = ${tum.length}`);
  console.log(`temiz kural: yasak = ${YASAK.size}, izinli = ${tum.length - YASAK.size}`);
  console.log(`  yasak kenarlar = ${[...YASAK].join(" ")}`);
  console.log(`  birlesimle ayni mi = ${YASAK.size === birlesim.size
    && [...YASAK].every((k) => birlesim.has(k))}`);
  for (const kok of process.argv.slice(2)) {
    const kenar = halkaKenarlari(kok);
    const ihlal = [...kenar].filter(([k]) => YASAK.has(k));
    console.log(`\n${kok}/  halka kenari = ${kenar.size}  (${[...kenar.keys()].sort().join(" ")})`);
    console.log(`  ihlal = ${ihlal.length}`);
    for (const [k, kaynak] of ihlal) console.log(`    ${k}  ${kaynak}`);
  }
}
```

Ağacın bir kopyasına iki küçük dosya ekleniyor: biri halka 2'de ham satırları kendisi ithal
ediyor, öteki halka 0'da sınır bildirimini okuyor.

```sh
cp -r temiz kirik
cat > kirik/bagdastiricilar/satir-onbellegi.mjs <<'EOF'
// kirik/bagdastiricilar/satir-onbellegi.mjs — halka 2: ham satirlari kendisi ithal ediyor
import { tarifeSatirlari } from "../altyapi/tarife-satirlari.mjs";

export const satirOnbellegi = () => tarifeSatirlari.satirlar();
EOF
cat > kirik/varliklar/alan-ozeti.mjs <<'EOF'
// kirik/varliklar/alan-ozeti.mjs — halka 0: sinir bildirimini kendisi okuyan yardimci
import { CIKTI_ALANLARI } from "../senaryolar/sinir.mjs";

export const ciktiAlanSayisi = () => CIKTI_ALANLARI.length;
EOF
node temiz-kural.mjs temiz kirik
```

```
olasi halka kenari = 16
temiz kural: yasak = 7, izinli = 9
  yasak kenarlar = 0->1 0->2 0->3 1->2 1->3 2->3 3->3
  birlesimle ayni mi = true

temiz/  halka kenari = 4  (1->0 1->1 2->1 3->2)
  ihlal = 0

kirik/  halka kenari = 6  (0->1 1->0 1->1 2->1 2->3 3->2)
  ihlal = 2
    2->3  bagdastiricilar/satir-onbellegi.mjs
    0->1  varliklar/alan-ozeti.mjs
```

Yasak küme birleşimle birebir aynı çıktı; katkı yeni bir yasak eklemek değil, iki yasağı tek
yüklemde toplamaktır. Temiz ağacın dört kenarı izinli dokuzun içinde. Eklenen iki dosya kenarı
altıya çıkardı ve ikisi ihlal: biri en dış halkaya giren, öteki en iç halkadan çıkan kenar.
Önceki dersin iki kuralı bunlardan **birer** tanesini görüyordu.

## Denetim Akışı ile Bağımlılık Okunun Ayrılması

Halka kenarı bir isteğin hangi yoldan geçtiğini söylemez: kaynakta bağımlılık içe bakarken
çalışma zamanında senaryo dışarıdaki geçidi çağırır. Betik bileşim kökünü kurar, her nesneyi bir
sarmalayıcıdan geçirir, çağrı anında yığıtın tepesindeki halkayı çağıran olarak kaydeder ve
kaydettiği her sınırı ithal kümesiyle karşılaştırır. Doğrudan ithalle yapılan çağrılar
kaydedilmiyor: onlarda iki yön tanım gereği aynıdır.

```js
// yon-ayrimi.mjs — bilesim kokunden gecen her sinirda denetim akisi ile bagimlilik okunu karsilastirir
import { halkaKenarlari } from "./temiz-kural.mjs";
import { tarifeSatirlari } from "./temiz/altyapi/tarife-satirlari.mjs";
import { masa } from "./temiz/altyapi/masa.mjs";
import { teklifDenetleyicisi } from "./temiz/bagdastiricilar/teklif-denetleyicisi.mjs";
import { tarifeGecidi, KAYNAK_ADLARI } from "./temiz/bagdastiricilar/tarife-gecidi.mjs";
import { teklifSenaryosu } from "./temiz/senaryolar/teklif-senaryosu.mjs";
import { GECIT_ADLARI } from "./temiz/senaryolar/sinir.mjs";

const ithal = new Set(halkaKenarlari("temiz").keys());
const yigit = [];
const cagri = new Map();

function sar(halka, nesne) {
  const sarili = {};
  for (const [ad, fn] of Object.entries(nesne)) {
    sarili[ad] = (...args) => {
      const cagiran = yigit.at(-1);
      if (cagiran !== undefined && cagiran !== halka) {
        const k = `${cagiran}->${halka}`;
        cagri.set(k, (cagri.get(k) ?? 0) + 1);
      }
      yigit.push(halka);
      try { return fn(...args); } finally { yigit.pop(); }
    };
  }
  return sarili;
}

const gecit = sar(2, tarifeGecidi(sar(3, tarifeSatirlari)));
const denetleyici = sar(2, teklifDenetleyicisi(sar(1, teklifSenaryosu(gecit))));
const disMasa = sar(3, masa(denetleyici));

const GOVDELER = [
  { agirlik: 3, hacim: 24000, bolge: "orta", beyanDegeri: 150000, sozlesme: "standart" },
  { agirlik: 60, hacim: 90000, bolge: "uzak", beyanDegeri: 400000, sozlesme: "hacimli" },
];
const yanit = GOVDELER.map((g) => disMasa.istek(g));
console.log(`istek = ${GOVDELER.length}  yanit = ${yanit.map((y) => `${y.durum} ${y.govde}`).join(" | ")}`);

const sayi = { ayni: 0, cevrilmis: 0, bagsiz: 0 };
console.log(`\nbilesim kokunden gecen sinir = ${cagri.size}`);
for (const [k, n] of [...cagri].sort()) {
  const [i, j] = k.split("->");
  const tur = ithal.has(k) ? "ayni" : ithal.has(`${j}->${i}`) ? "cevrilmis" : "bagsiz";
  sayi[tur] += 1;
  const ok = tur === "ayni" ? k : tur === "cevrilmis" ? `${j}->${i}` : "yok";
  console.log(`  denetim ${k}  bagimlilik ${ok}  ${tur.padEnd(9)} cagri = ${n}`);
}
console.log(`ayni yon = ${sayi.ayni}, cevrilmis = ${sayi.cevrilmis}, bagsiz = ${sayi.bagsiz}`);
console.log(`cevrilmis sinirlarda ice bildirilen ad: gecit ${GECIT_ADLARI.length}, kaynak ${KAYNAK_ADLARI.length}`);
```

```sh
node yon-ayrimi.mjs
```

```
istek = 2  yanit = 200 114.50 TL / 4.80 kg | 422 agirlik kademesi yok

bilesim kokunden gecen sinir = 4
  denetim 1->2  bagimlilik 2->1  cevrilmis cagri = 6
  denetim 2->1  bagimlilik 2->1  ayni      cagri = 2
  denetim 2->3  bagimlilik 3->2  cevrilmis cagri = 6
  denetim 3->2  bagimlilik 3->2  ayni      cagri = 2
ayni yon = 2, cevrilmis = 2, bagsiz = 0
cevrilmis sinirlarda ice bildirilen ad: gecit 3, kaynak 3
```

Dört sınırın ikisinde iki yön aynı: masa denetleyiciyi hem çağırıyor hem ithal ediyor,
denetleyici de senaryoyu. İkisinde ayrılıyor: senaryo geçidi çağırıyor ama geçit senaryoyu ithal
ediyor; geçit ham kaynağı çağırıyor ama kaynağın halkası geçidin halkasını ithal ediyor. Ayrımı
olanaklı kılan, çağrılan adların **içeride** bildirilmiş olmasıdır — üç ad halka 1'de, üç ad
halka 2'de yazılı. Bedel de aynı sayıdadır: bu iki sınırda çalışma zamanı yolu ithal
satırlarından okunamaz, yalnız bileşim köküne bakılarak bulunur.

## Sınırdan Geçen Veri

İkinci ölçü sınırdan geçen verinin biçimidir. Senaryo, kuralın döndürdüğü nesneyi olduğu gibi
dışa verebilir ya da bildirilmiş alanlara daraltabilir. Betik ikisini erişilebilir ad,
bildirilmemiş ad ve JSON karakteri sayısıyla ölçüyor.

```js
// sinir-verisi.mjs — sinirdan gecen iki veri bicimini erisilebilir ad ve karakter sayisiyla olcer
import { gonderi } from "./temiz/varliklar/gonderi.mjs";
import { ucretKurali } from "./temiz/varliklar/ucret-kurali.mjs";
import { CIKTI_ALANLARI, bildirilmemisAd } from "./temiz/senaryolar/sinir.mjs";
import { teklifSenaryosu } from "./temiz/senaryolar/teklif-senaryosu.mjs";
import { teklifDenetleyicisi } from "./temiz/bagdastiricilar/teklif-denetleyicisi.mjs";

const sahteGecit = {
  kademeler: () => [{ ustAgirlik: 1, ucret: 4990 }, { ustAgirlik: 5, ucret: 8490 }],
  katsayi: (bolge) => (bolge === "orta" ? 1.35 : 1),
  indirimOrani: (sozlesme) => (sozlesme === "standart" ? 0.05 : 0),
};
const GIRDI = { agirlik: 3, hacim: 24000, bolge: "orta", beyanDegeri: 150000, sozlesme: "standart" };

const g = gonderi(GIRDI);
const ham = { ...ucretKurali.hesapla(g, sahteGecit.kademeler(), sahteGecit.katsayi(g.bolge),
  sahteGecit.indirimOrani(g.sozlesme)), gonderi: g };
const cikti = teklifSenaryosu(sahteGecit).calistir(GIRDI);

for (const [ad, veri] of [["kuralin nesnesi", ham], ["cikti modeli", cikti]]) {
  const adlar = bildirilmemisAd(veri, []);
  const bildirilmemis = bildirilmemisAd(veri, CIKTI_ALANLARI);
  console.log(`${ad.padEnd(16)} erisilebilir ad = ${String(adlar.length).padStart(2)}`
    + `  bildirilmemis = ${String(bildirilmemis.length).padStart(2)}`
    + `  json karakteri = ${String(JSON.stringify(veri).length).padStart(3)}`);
  for (let i = 0; i < bildirilmemis.length; i += 4) {
    console.log(`  bildirilmemis: ${bildirilmemis.slice(i, i + 4).join(", ")}`);
  }
}

const yanlis = teklifDenetleyicisi({ calistir: () => ham });
const dogru = teklifDenetleyicisi(teklifSenaryosu(sahteGecit));
console.log(`\nkuralin nesnesi disa verilirse -> durum ${yanlis.teklifIstegi(GIRDI).durum}`);
const y = dogru.teklifIstegi(GIRDI);
console.log(`cikti modeli disa verilirse    -> durum ${y.durum}  ${y.govde}`);
```

```sh
node sinir-verisi.mjs
```

```
kuralin nesnesi  erisilebilir ad = 10  bildirilmemis =  8  json karakteri = 174
  bildirilmemis: kademe.ustAgirlik, kademe.ucret, gonderi.agirlik, gonderi.hacim
  bildirilmemis: gonderi.bolge, gonderi.beyanDegeri, gonderi.sozlesme, gonderi.ucretlenenAgirlik
cikti modeli     erisilebilir ad =  4  bildirilmemis =  0  json karakteri =  81

kuralin nesnesi disa verilirse -> durum 500
cikti modeli disa verilirse    -> durum 200  114.50 TL / 4.80 kg
```

Kuralın nesnesinde dışarıdan erişilebilen 10 ad var ve sekizi çıktı sözleşmesinde bildirilmemiş:
ikisi kademe nesnesinin, altısı varlık nesnesinin iç alanları. Erişilebilir ad 10'dan 4'e, JSON
karakteri 174'ten 81'e iniyor.

Fark boyut değil, bilme yükümlülüğüdür. Varlık nesnesi sınırdan geçtiğinde halka 2, halka 0'ın
alan adlarını okur; kural bir alanı yeniden adlandırdığında bağdaştırıcı da değişir. Bu bağ ithal
grafiğinde görünmez, çünkü ortada ithal yoktur — bağ nesnenin biçimindedir. Denetleyicinin çıktıyı
bildirilmiş alanlara karşı sınaması bunu koşum anında yakalıyor: yanıt 500'e karşı 200.

## Ödünleşimin Sayıları

Kazanç iki kalite niteliğinde ölçüldü. Sürdürülebilirlikte: sekiz dosyalı ağacın dört halka
kenarı izinli dokuzun içinde ve ihlal 0, eklenen iki küçük dosya ihlali 2'ye çıkardı.
Sınanabilirlikte: halka 1'den çıkan kenarlar `1->0` ve `1->1` olduğu için senaryo elle yazılmış
üç işlevlik bir geçitle koşuyor.

Bedel aynı sayılardadır: dört sınırın ikisi çevrilmiş ve her çevrilmiş sınır içeride bir bildirim
ile dışarıda onu karşılayan bir tanım istiyor. Kenar kümesi ne kadar sıkıysa ihlal o kadar erken
yakalanır, çevirme noktası sayısı da o kadar yüksek olur.

## Özet

- Kural iki koşulun çarpımıdır: bir halka yalnız kendinden içteki ya da kendi halkasını ithal
  edebilir ve en dış halkaya hiçbir kenar giremez; on altı kenardan yedisi yasaklanır ve yasak
  küme iki halka kuralının birleşimiyle birebir aynıdır.
- Temiz ağacın dört kenarında ihlal 0; eklenen iki dosya en dış halkaya giren ve en iç halkadan
  çıkan iki kenar üretti, ihlal 2'ye çıktı.
- Bileşim kökünden geçen dört sınırın ikisinde denetim akışı ile bağımlılık oku ayrıldı; ayrımı
  çağrılan altı adın içeride bildirilmesi olanaklı kıldı.
- Kuralın nesnesi sınırdan geçtiğinde erişilebilir ad 10 ve bildirilmemiş ad 8, çıktı modeli
  geçtiğinde 4 ve 0; JSON karakteri 174'ten 81'e indi.

## Sonraki Adım

Bu dersin bütün ölçüleri **halka** üzerindeydi. Halka bir kural birimidir, yayın birimi değildir:
sekiz dosyanın hangi kümesinin birlikte paketlendiği, hangisinin ayrı sürümlenebildiği ve bir
tüketicinin tek yetenek için kaç dosyaya bağlandığı hiçbir ölçüde görünmüyor. Sonraki ders birimi
halkadan **bileşene** taşır: aynı kitaplığı dağıtılabilir birimlere böler, her bileşenin dışa
açtığı ad sayısı ile iki bileşenin birlikte değişme oranını ölçer.
