---
title: 'Bağımlılık Çizgesi Sağlığı'
source: 'https://academia.sh/tr/kurslar/tasarim-ilkeleri/bagimlilik-cizgesi-sagligi'
course: 'Tasarım İlkeleri'
language: tr
updated: '2026-08-17T18:10:40+00:00'
license: 'CC BY-SA 4.0'
---

# Bağımlılık Çizgesi Sağlığı

İthal grafiğinin kaynak dosyalardan çıkarılması ve bileşen düzeyinde denetlenmesi: derine aramayla döngülerin adlarıyla listelenmesi, katman sıralarına aykırı kenarların sayılması, döngünün çalışma zamanındaki karşılığının gösterilmesi ve onarımdan sonra iki sayının da sıfıra inmesi.

Bir önceki ders yayın sırasının bozulmasından döngünün **var olduğunu** okudu ama yerini
söyleyemedi: sıralanamayan üç bileşenin hangisi döngünün içinde, hangisi ona bağlı olduğu
için listede kaldı, çıktıdan anlaşılmıyordu. Ayrıca oradaki bütün ölçüler elle yazılmış bir
bileşen çizgesi üzerindeydi.

Gerçek bir kod tabanında çizge elle yazılmaz, ithal satırlarından çıkarılır. Bu ders o
çıkarımı yapan, döngüleri adlarıyla listeleyen ve katman sırasına aykırı kenarları sayan bir
araç yazar; aracı önce bozuk bir sürümde koşturur, sonra onarılmış sürümde iki sayının da
sıfıra indiğini gösterir.

## Denetlenecek Kaynak Ağacı

İthal grafiğinin dosya düzeyinde çıkarılması ve katman kuralının denetlenmesi Veri Erişim
Katmanı ve İş Mantığı kursundaki Katman Sorumlulukları dersinde kurulmuştu. Buradaki iş
farklıdır: aynı grafik **bileşen düzeyine** indirgenir ve üzerinde döngü aranır. Katman
denetimi de bileşen sıralarına göre yapılır, dosya yollarına göre değil.

Aşağıdaki blok kitaplığın bozuk sürümünü kurar. Dokuz dosya, beş bileşen ve dört katman
sırası var: `para` en altta (0), `tarife` ile `ag` bir üstte (1), `ticari-kural` onların
üstünde (2), giriş dosyası en üstte (3).

```sh
mkdir -p bozuk
cat > bozuk/para.mjs <<'EOF'
import { KADEMELER } from "./tarife-tablosu.mjs";
export const kurus = (tl) => Math.round(tl * 100);
export const ENUST_AGIRLIK = KADEMELER.at(-1).ustAgirlik;
EOF
cat > bozuk/tarife-tablosu.mjs <<'EOF'
import { kurus } from "./para.mjs";
export const KADEMELER = [{ ustAgirlik: 5, ucret: kurus(84.9) }, { ustAgirlik: 30, ucret: kurus(249.9) }];
EOF
cat > bozuk/katsayi.mjs <<'EOF'
import { kurus } from "./para.mjs";
export const KATSAYI = { yakin: 1, orta: 1.35, uzak: 1.8 };
export const ASGARI = kurus(39.9);
EOF
cat > bozuk/ucret-kurali.mjs <<'EOF'
import { KADEMELER } from "./tarife-tablosu.mjs";
import { KATSAYI, ASGARI } from "./katsayi.mjs";
export const ucret = (agirlik, bolge) =>
  Math.max((KADEMELER.find((k) => agirlik <= k.ustAgirlik)?.ucret ?? 0) * KATSAYI[bolge], ASGARI);
EOF
cat > bozuk/indirim.mjs <<'EOF'
import { kurus } from "./para.mjs";
export const indirimli = (tutar, oran) => Math.max(Math.round(tutar * (1 - oran)), kurus(39.9));
EOF
cat > bozuk/secim.mjs <<'EOF'
import { ucret } from "./ucret-kurali.mjs";
import { indirimli } from "./indirim.mjs";
import { TASIYICILAR } from "./tasiyici-listesi.mjs";
export const enUcuz = (agirlik, bolge, oran) => TASIYICILAR
  .map((t) => ({ ad: t.ad, tutar: indirimli(ucret(agirlik, bolge) * t.carpan, oran) }))
  .sort((a, b) => a.tutar - b.tutar)[0];
EOF
cat > bozuk/tasiyici-listesi.mjs <<'EOF'
import { enUcuz } from "./secim.mjs";
export const TASIYICILAR = [{ ad: "standart", carpan: 1 }, { ad: "ekonomik", carpan: 0.85 }];
export const varsayilan = (agirlik) => enUcuz(agirlik, "orta", 0).ad;
EOF
cat > bozuk/rota-kurma.mjs <<'EOF'
import { TASIYICILAR } from "./tasiyici-listesi.mjs";
export const rota = (bolge) => [bolge, TASIYICILAR[0].ad];
EOF
cat > bozuk/dene.mjs <<'EOF'
import { enUcuz } from "./secim.mjs";
console.log("enUcuz(3, 'orta', 0) =", JSON.stringify(enUcuz(3, "orta", 0)));
EOF
ls bozuk | tr '\n' ' '
```

```
dene.mjs indirim.mjs katsayi.mjs para.mjs rota-kurma.mjs secim.mjs tarife-tablosu.mjs tasiyici-listesi.mjs ucret-kurali.mjs 
```

İki ithal satırı fazladan konmuş durumda: `para.mjs` tarife tablosunu ithal ediyor ve
`tasiyici-listesi.mjs` seçim modülünü ithal ediyor. İkisi de tek başına bakıldığında masum
görünür — para modülü en üst ağırlık kademesini bilmek istiyor, taşıyıcı listesi varsayılan
taşıyıcıyı hesaplamak istiyor.

## Döngünün Çalışma Zamanındaki Karşılığı

Bu iki satırın bedeli çizge üzerinde kalmıyor. Giriş dosyası çalıştırıldığında modüller
birbirini bekliyor.

```sh
node bozuk/dene.mjs 2>&1 | grep -m1 "^ReferenceError"
```

```
ReferenceError: Cannot access 'KADEMELER' before initialization
```

Hata, döngüdeki modüllerden birinin gövdesi çalışırken diğerinin dışa açtığı sabitin henüz
tanımlanmamış olmasından çıkıyor. Hangi modülün önce değerlendirileceği giriş noktasına bağlı
olduğu için, aynı döngü başka bir giriş noktasında başka bir adla ya da hiç hata vermeden
geçebilir. Döngünün en zor tarafı budur: belirtisi kararlı değildir.

## Döngü Tespiti ve Katman Denetimi

Araç üç adım yapar. Önce her dosyanın ithal satırlarını okuyup bileşen kenarlarını çıkarır;
aynı bileşen içindeki ithaller elenir. Sonra bu kenarlar üzerinde **derine arama** yapar —
Veri Yapıları kursundaki Derine Arama dersinde kurulan üç renkli düzen: gri düğüm hâlen
işlenmektedir, siyah düğüm bitmiştir, gri bir düğüme geri gelinmesi döngüdür. Son olarak her
kenarın katman sıralarını karşılaştırır.

```js
// cizge-sagligi.mjs — ithal grafiginden bilesen dongusunu ve katman ihlalini cikarir
import { readdirSync, readFileSync } from "node:fs";

const BILESEN = {
  "para.mjs": "para", "tarife-tablosu.mjs": "tarife", "katsayi.mjs": "tarife",
  "ucret-kurali.mjs": "ticari-kural", "indirim.mjs": "ticari-kural", "secim.mjs": "ticari-kural",
  "tasiyici-listesi.mjs": "ag", "rota-kurma.mjs": "ag", "dene.mjs": "giris",
};
const KATMAN = { para: 0, tarife: 1, ag: 1, "ticari-kural": 2, giris: 3 };

function bilesenKenarlari(dizin) {
  const kenar = new Set();
  for (const dosya of readdirSync(dizin).filter((d) => d.endsWith(".mjs")).sort()) {
    const metin = readFileSync(`${dizin}/${dosya}`, "utf8");
    for (const m of metin.matchAll(/^import\s.*?from\s+"\.\/([\w.-]+)"/gm)) {
      const [a, b] = [BILESEN[dosya], BILESEN[m[1]]];
      if (a !== b) kenar.add(`${a} ${b}`);
    }
  }
  return [...kenar].map((k) => k.split(" "));
}

function dongular(kenarlar) {
  const komsu = new Map();
  for (const [a, b] of kenarlar) komsu.set(a, [...(komsu.get(a) ?? []), b]);
  const durum = new Map(), yol = [], bulunan = [];
  const derineAra = (d) => {
    durum.set(d, "gri"); yol.push(d);
    for (const k of komsu.get(d) ?? []) {
      if (durum.get(k) === "gri") bulunan.push([...yol.slice(yol.indexOf(k)), k]);
      else if (durum.get(k) !== "siyah") derineAra(k);
    }
    yol.pop(); durum.set(d, "siyah");
  };
  for (const d of [...new Set(kenarlar.flat())].sort()) if (!durum.has(d)) derineAra(d);
  return bulunan;
}

for (const dizin of process.argv.slice(2)) {
  const kenarlar = bilesenKenarlari(dizin);
  const dongu = dongular(kenarlar);
  const ihlal = kenarlar.filter(([a, b]) => KATMAN[a] < KATMAN[b]);
  console.log(`${dizin}/  bilesen kenari = ${kenarlar.length}`);
  console.log(`  dongu = ${dongu.length}`);
  for (const d of dongu) console.log(`    ${d.join(" -> ")}`);
  console.log(`  katman ihlali = ${ihlal.length}`);
  for (const [a, b] of ihlal) console.log(`    ${a}(${KATMAN[a]}) -> ${b}(${KATMAN[b]})`);
}
```

```sh
node cizge-sagligi.mjs bozuk
```

```
bozuk/  bilesen kenari = 7
  dongu = 2
    para -> tarife -> para
    ag -> ticari-kural -> ag
  katman ihlali = 2
    para(0) -> tarife(1)
    ag(1) -> ticari-kural(2)
```

Bir önceki dersin yayın sırası üç bileşeni "sıralanamayan" diye vermişti; buradaki çıktı
döngünün içindeki bileşenleri ayrı ayrı adlandırıyor ve `ticari-kural`'ın döngüde değil,
döngüye bağlı olduğunu ortaya koyuyor.

İki sayı aynı iki kenarı işaret ediyor ama aynı şeyi ölçmüyor. Katman ihlali bir **kural**
ihlalidir: sıralar önceden ilan edilmiştir, kenar onlara uymamıştır. Döngü ise kuraldan
bağımsızdır; hiçbir katman ilan edilmemiş olsa da döngü yine bir kusurdur, çünkü yayın sırası
ve değerlendirme sırası ondan bağımsız olarak bozulur. Katman kuralı olmayan bir kitaplıkta
ikinci sayı hesaplanamaz, birincisi hesaplanır.

## Onarım

İki ithal iki farklı hareketle kaldırılıyor. `para.mjs` en üst ağırlık kademesini ithal
etmek yerine kademeleri parametre olarak alıyor — birinci dersteki hareketin aynısı, kural
tabloyu ithal etmez. `tasiyici-listesi.mjs` içindeki varsayılan taşıyıcı hesabı ise seçim
modülüne taşınıyor; hesap zaten seçime bağlıydı, listeye değil.

```sh
cp -r bozuk duzeltilmis
cat > duzeltilmis/para.mjs <<'EOF'
export const kurus = (tl) => Math.round(tl * 100);
export const enUstAgirlik = (kademeler) => kademeler.at(-1).ustAgirlik;
EOF
cat > duzeltilmis/tasiyici-listesi.mjs <<'EOF'
export const TASIYICILAR = [{ ad: "standart", carpan: 1 }, { ad: "ekonomik", carpan: 0.85 }];
EOF
cat >> duzeltilmis/secim.mjs <<'EOF'
export const varsayilan = (agirlik) => enUcuz(agirlik, "orta", 0).ad;
EOF
node duzeltilmis/dene.mjs
node cizge-sagligi.mjs duzeltilmis
```

```
enUcuz(3, 'orta', 0) = {"ad":"ekonomik","tutar":9742}
duzeltilmis/  bilesen kenari = 5
  dongu = 0
  katman ihlali = 0
```

Giriş dosyası artık hata vermeden çalışıyor ve bir sonuç üretiyor. Bileşen kenarı yediden
beşe, döngü ikiden sıfıra, katman ihlali ikiden sıfıra indi. Kaybedilen bir yetenek yok:
varsayılan taşıyıcı hesabı hâlâ var, yalnız başka bir dosyada.

## Sayının Eşik Olması

Bu iki sayının değeri, tek seferlik bir ölçüm olmamalarındadır. Döngüler tek bir büyük
kararla değil, tek tek masum görünen ithal satırlarıyla birikir; bozuk sürümdeki iki satırın
ikisi de yazıldıkları anda makul bir gerekçeye sahipti. Denetim her değişiklikte
koşturulduğunda ve döngü sayısı için eşik sıfır kabul edildiğinde, gerekçe tartışması satır
yazılmadan önce yapılır.

Aracın çıkardığı çizge, bir önceki dersin kararsızlık ölçüsünün de girdisidir. Aynı ithal
grafiğinden üç sayı okunur: döngü sayısı, katman ihlali sayısı ve bileşen başına kararsızlık.
Üçü birden sıfıra ya da beklenen yöne oturmuşsa bileşen sınırları çalışıyor demektir.

## Özet

- Bileşen çizgesi elle yazılmaz; ithal satırlarından çıkarılır ve aynı bileşen içindeki
  ithaller elenerek bileşen kenarlarına indirgenir.
- Döngü, derine aramanın üç renkli düzeniyle bulunur: gri bir düğüme geri dönülmesi döngüdür
  ve yığıttaki yol döngünün üyelerini adlarıyla verir.
- Bozuk sürümde iki ithal satırı iki döngü ve iki katman ihlali üretti; aynı döngü çalışma
  zamanında giriş noktasına bağlı bir başlatma hatasına dönüştü.
- Katman ihlali ilan edilmiş bir kuralın ihlalidir, döngü ise kuraldan bağımsız bir
  kusurdur; katman kuralı olmayan bir kitaplıkta da döngü sayılabilir.
- Onarımdan sonra bileşen kenarı 7'den 5'e, döngü ve katman ihlali 2'den 0'a indi; taşınan
  hesap kaybolmadı, yalnız bağımlılığın zaten var olduğu tarafa geçti.

## Kurs Kapanışı

Kurs bir tasarımın iyi olup olmadığını birimin içine değil, birimler arasındaki bağa bakarak
ölçtü ve her bölümde ölçüyü bir düzey yukarı taşıdı.

**SOLID** bölümü sınıf ve modül düzeyinde beş ölçüt kurdu: sorumluluğun iş miktarı değil
değişim nedeni sayısı olarak sayılması, davranışın var olan kodu düzenlemeden genişletilmesi,
bir alt tipin üst tipin sözleşmesini bozmadan yerine geçmesi, arayüzlerin istemcinin fiilen
kullandığı kadarıyla sınırlanması ve bağımlılığın somut ayrıntıya değil soyutlamaya
kurulması.

**Bağlaşım ve Uyum** bölümü modül çiftlerine geçti: iki modül arasındaki bağın gücünün
ölçülmesi, bir modül içindeki adların birbiriyle ilgililiği, bir çağrının kaç nesne üzerinden
geçtiğinin sınırlanması, veriyi çekip karar vermek yerine kararı veriye söylemek, durum
değiştiren işlemlerle okuyanların ayrılması, değişim eksenlerinin ayrı ayrı kapsüllenmesi,
somut tipe bağlanmanın maliyeti ve denetimin çağrılan tarafa devredilmesi.

**Sınırlar ve Bileşen İlkeleri** bölümü ölçeği dosyadan bileşene çıkardı: kararlı politikanın
değişken ayrıntıdan değişim sıklığıyla ayrılması, sözleşme yüzeyinin ad ve alan sayısıyla
ölçülüp daraltılması, sözleşmesi başkasının denetiminde olan bir aracın bağdaştırıcı arkasına
alınması, bileşen sınırının birlikte değişen dosyalardan çıkarılması, bağların kararlı yöne
akıp akmadığının sayılması ve bağımlılık çizgesinde döngü ile katman ihlalinin bulunması.

Kursun baştan sona koruduğu bir varsayım var ve kapanışta adlandırılması gerekiyor: ilkeler
bir tasarımın **nasıl olması gerektiğini** söyler, ne yapılacağını değil. "Sınır dar olsun",
"bağ kararlı yöne aksın", "birlikte değişenler bir arada dursun" cümlelerinin hiçbiri bir
çözüm vermez; ölçüt verir. Oysa aynı sınır problemi farklı kod tabanlarında defalarca aynı
biçimde çözülmüşse, o çözümün bir adı, bilinen bir uygulanış biçimi ve bilinen sonuçları
vardır.

Sonraki kurs **Tasarım Kalıpları** bunu ele alır: klasik kalıpların çözdükleri probleme göre
sınıflandırılması, bir kalıbın gereksiz karmaşıklık üretmeden — yani ilkelerin ölçüleri
kötüleşmeden — uygulanması ve kurumsal uygulama kalıplarının veri erişim tasarımında
kullanılması. Bu kursun ölçüleri orada da geçerlidir; kalıp, ölçüyü iyileştirdiği ölçüde
kalıptır.
