---
title: 'Bağımlılığın Ters Çevrilmesi'
source: 'https://academia.sh/tr/kurslar/tasarim-ilkeleri/bagimliligin-ters-cevrilmesi'
course: 'Tasarım İlkeleri'
language: tr
updated: '2026-08-17T18:10:40+00:00'
license: 'CC BY-SA 4.0'
---

# Bağımlılığın Ters Çevrilmesi

Soyutlamanın hangi tarafta tanımlandığının sayıya çevrilmesi: kural ve taşıyıcı paketleri arasındaki ithal kenarlarının üç sürümde yönüyle sayılması, sözleşmenin sahibi ile giren kenar sayısının ölçülmesi ve taşıyıcı paketi olmadan kural modülünün yüklenip yüklenemediğinin koşumla sınanması.

Bölünmüş sözleşmeler istemcilerin yanında durdu, ama bu yerleşim ayrı bir karardı. Arayüzün
kaç parçaya bölündüğü ile o parçaların hangi pakette tanımlandığı birbirinden bağımsızdır:
küçük bir sözleşme de gerçekleştirimin yanında yaşayabilir. O durumda istemci küçük bir
arayüze bağlanır ama yine gerçekleştirimin bulunduğu paketi ithal eder.

**Bağımlılığın tersine çevrilmesi** (dependency inversion) SOLID'in beşinci maddesidir ve tam
bu soruyu ele alır: üst düzey kural, alt düzey gerçekleştirime değil, ikisinin de bağlandığı
bir soyutlamaya bağlanmalıdır; soyutlama da gerçekleştirimin değil kuralın tanımladığı bir şey
olmalıdır. Veri Erişim Katmanı ve İş Mantığı kursunda bu kural katmanlar arasındaki ithal
grafiği üzerinden denetlenmiş, ihlal sayısı ve ithal kapanışının boyutu ölçülmüştü. Buradaki
soru daha dar ve ölçüsü de farklı: iki paket arasındaki kenarlar hangi yöne bakıyor ve
sözleşme dosyası hangi pakette duruyor?

## Üç Yerleşim

Kod tabanı iki pakete ayrılmıştır. `kural/` ücret kuralını, `tasiyici/` taşıyıcıya özgü taban
ücret hesaplarını tutar. Aynı davranış üç ayrı yerleşimle yazılır.

```sh
mkdir -p dogrudan/kural dogrudan/tasiyici arada/kural arada/tasiyici
mkdir -p tersine/kural tersine/tasiyici
```

Birinci yerleşimde soyutlama hiç yoktur; kural iki somut modülü doğrudan ithal eder ve
aralarında kendisi seçim yapar.

```js
// dogrudan/tasiyici/yurtici.mjs — yurtici tasiyicinin taban ucreti
export const yurticiTaban = (g) => (g.agirlik <= 1 ? 3900 : g.agirlik <= 5 ? 6400 : 11800);
```

```js
// dogrudan/tasiyici/ekspres.mjs — ekspres tasiyicinin taban ucreti
export const ekspresTaban = (g) => 5200 + Math.ceil(g.agirlik) * 900;
```

```js
// dogrudan/kural/ucret.mjs — ust duzey kural iki somut tasiyiciyi kendisi ithal eder
import { yurticiTaban } from "../tasiyici/yurtici.mjs";
import { ekspresTaban } from "../tasiyici/ekspres.mjs";

const BOLGE = { "34": 100, "06": 115, "65": 140 };
const katsayi = (adres) => BOLGE[adres.slice(0, 2)] ?? 160;

export function ucret(gonderi) {
  const taban = gonderi.tasiyici === "ekspres" ? ekspresTaban(gonderi) : yurticiTaban(gonderi);
  return Math.round((taban * katsayi(gonderi.adres)) / 100);
}
```

İkinci yerleşimde bir sözleşme vardır, ama taşıyıcı paketinde tanımlıdır.

```js
// arada/tasiyici/sozlesme.mjs — sozlesme var, ama tasiyici paketinde tanimli
export function tasiyiciyiDogrula(t) {
  if (typeof t?.taban !== "function") throw new TypeError("tasiyici sozlesmesi eksik: taban");
  return t;
}
```

```js
// arada/tasiyici/yurtici.mjs — sozlesmeyi karsilayan yurtici tasiyici
import { tasiyiciyiDogrula } from "./sozlesme.mjs";

export const yurtici = tasiyiciyiDogrula({
  ad: "yurtici",
  taban: (g) => (g.agirlik <= 1 ? 3900 : g.agirlik <= 5 ? 6400 : 11800),
});
```

```js
// arada/tasiyici/ekspres.mjs — sozlesmeyi karsilayan ekspres tasiyici
import { tasiyiciyiDogrula } from "./sozlesme.mjs";

export const ekspres = tasiyiciyiDogrula({
  ad: "ekspres",
  taban: (g) => 5200 + Math.ceil(g.agirlik) * 900,
});
```

```js
// arada/kural/ucret.mjs — kural sozlesmeye baglanir, ama sozlesme tasiyici paketinde
import { tasiyiciyiDogrula } from "../tasiyici/sozlesme.mjs";

const BOLGE = { "34": 100, "06": 115, "65": 140 };
const katsayi = (adres) => BOLGE[adres.slice(0, 2)] ?? 160;

export function ucret(tasiyici, gonderi) {
  tasiyiciyiDogrula(tasiyici);
  return Math.round((tasiyici.taban(gonderi) * katsayi(gonderi.adres)) / 100);
}
```

Üçüncü yerleşimde sözleşme dosyası olduğu gibi kural paketine taşınır. Dosyanın içeriği
değişmez, yalnız yeri değişir.

```js
// tersine/kural/sozlesme.mjs — sozlesme kural paketinde tanimli
export function tasiyiciyiDogrula(t) {
  if (typeof t?.taban !== "function") throw new TypeError("tasiyici sozlesmesi eksik: taban");
  return t;
}
```

```js
// tersine/kural/ucret.mjs — kural yalniz kendi sozlesmesini ithal eder
import { tasiyiciyiDogrula } from "./sozlesme.mjs";

const BOLGE = { "34": 100, "06": 115, "65": 140 };
const katsayi = (adres) => BOLGE[adres.slice(0, 2)] ?? 160;

export function ucret(tasiyici, gonderi) {
  tasiyiciyiDogrula(tasiyici);
  return Math.round((tasiyici.taban(gonderi) * katsayi(gonderi.adres)) / 100);
}
```

```js
// tersine/tasiyici/yurtici.mjs — gerceklestirim, kuralin sozlesmesini ithal eder
import { tasiyiciyiDogrula } from "../kural/sozlesme.mjs";

export const yurtici = tasiyiciyiDogrula({
  ad: "yurtici",
  taban: (g) => (g.agirlik <= 1 ? 3900 : g.agirlik <= 5 ? 6400 : 11800),
});
```

```js
// tersine/tasiyici/ekspres.mjs — gerceklestirim, kuralin sozlesmesini ithal eder
import { tasiyiciyiDogrula } from "../kural/sozlesme.mjs";

export const ekspres = tasiyiciyiDogrula({
  ad: "ekspres",
  taban: (g) => 5200 + Math.ceil(g.agirlik) * 900,
});
```

Üç sürüm de aynı ücreti üretir.

```js
// ayni-sonuc.mjs — uc surum ayni ucreti uretir; fark davranista degil
const GONDERI = { tasiyici: "ekspres", agirlik: 3.0, adres: "06500" };

const { ucret: dogrudanUcret } = await import("./dogrudan/kural/ucret.mjs");
const { ucret: aradaUcret } = await import("./arada/kural/ucret.mjs");
const { ekspres: aradaEkspres } = await import("./arada/tasiyici/ekspres.mjs");
const { ucret: tersineUcret } = await import("./tersine/kural/ucret.mjs");
const { ekspres: tersineEkspres } = await import("./tersine/tasiyici/ekspres.mjs");

console.log("dogrudan =", dogrudanUcret(GONDERI), "kurus");
console.log("arada    =", aradaUcret(aradaEkspres, GONDERI), "kurus");
console.log("tersine  =", tersineUcret(tersineEkspres, GONDERI), "kurus");
```

```sh
node ayni-sonuc.mjs
```

```
dogrudan = 9085 kurus
arada    = 9085 kurus
tersine  = 9085 kurus
```

## Kenarların Yönle Sayılması

Fark yalnız ithal grafiğinde görünür. Aşağıdaki betik iki paketteki bütün modülleri okur,
göreli ithal yollarını çözer ve paket sınırını geçen kenarları yönüne göre sayar. İkinci
ölçüsü soyutlamanın kendisiyle ilgilidir: sözleşme dosyası hangi pakette duruyor ve kaç modül
ona bağlanıyor?

```js
// yon-olc.mjs — iki paket arasindaki ithal kenarlarini ve soyutlamanin sahibini sayar
import { readdirSync, readFileSync } from "node:fs";
import { join, relative, resolve, dirname } from "node:path";

const ITHAL = /^\s*(?:import|export)[^;'"]*from\s+["'](\.[^"']+)["']/gm;
const paket = (yol) => yol.split("/")[0];
const kok = process.argv[2];
const kenarlar = [];

for (const p of ["kural", "tasiyici"]) {
  for (const ad of readdirSync(join(kok, p)).filter((a) => a.endsWith(".mjs")).sort()) {
    const kaynak = `${p}/${ad}`;
    for (const m of readFileSync(join(kok, kaynak), "utf8").matchAll(ITHAL)) {
      kenarlar.push([kaynak, relative(kok, resolve(kok, dirname(kaynak), m[1]))]);
    }
  }
}

const disa = kenarlar.filter(([a, b]) => paket(a) !== paket(b));
for (const [a, b] of disa) console.log(`  ${a} -> ${b}`);
const say = (p) => disa.filter(([a]) => paket(a) === p).length;
console.log(`${kok.padEnd(9)} kural -> tasiyici = ${say("kural")}` +
  `   tasiyici -> kural = ${say("tasiyici")}`);

const soyutlamalar = [...new Set(kenarlar.map(([, b]) => b))].filter((b) => b.endsWith("sozlesme.mjs"));
if (soyutlamalar.length === 0) {
  console.log(`${kok.padEnd(9)} soyutlama tanimli degil`);
}
for (const s of soyutlamalar) {
  const giren = kenarlar.filter(([, b]) => b === s).length;
  console.log(`${kok.padEnd(9)} soyutlamanin sahibi = ${paket(s)}, giren kenar = ${giren}`);
}
```

```sh
node yon-olc.mjs dogrudan
node yon-olc.mjs arada
node yon-olc.mjs tersine
```

```
  kural/ucret.mjs -> tasiyici/yurtici.mjs
  kural/ucret.mjs -> tasiyici/ekspres.mjs
dogrudan  kural -> tasiyici = 2   tasiyici -> kural = 0
dogrudan  soyutlama tanimli degil
  kural/ucret.mjs -> tasiyici/sozlesme.mjs
arada     kural -> tasiyici = 1   tasiyici -> kural = 0
arada     soyutlamanin sahibi = tasiyici, giren kenar = 3
  tasiyici/ekspres.mjs -> kural/sozlesme.mjs
  tasiyici/yurtici.mjs -> kural/sozlesme.mjs
tersine   kural -> tasiyici = 0   tasiyici -> kural = 2
tersine   soyutlamanin sahibi = kural, giren kenar = 3
```

Üç satır üç ayrı durumu ayırıyor. Birinci sürümde kuraldan gerçekleştirime iki kenar var ve
soyutlama yok. İkinci sürümde soyutlama var, giren kenar sayısı üç — yani sözleşme gerçekten
işini görüyor — ama sınırı geçen kenar sayısı ikiden bire indi, sıfıra inmedi: kural hâlâ
taşıyıcı paketini ithal ediyor. Üçüncü sürümde aynı sözleşme, aynı üç giren kenarla, kural
paketinde duruyor ve sınırı geçen kenarların tamamı ters yöne bakıyor.

İkinci sürüm ilkenin en sık yapılan yarım uygulamasıdır. Soyutlamanın var olması yetmez;
**kimin tanımladığı** ölçünün asıl belirleyicisidir.

## Gerçekleştirim Olmadan Kural

Kenar yönü soyut bir ölçü gibi görünür; karşılığı somuttur. Her sürümden yalnız kural
paketini içeren bir kopya çıkarılır ve kural modülü tek başına yüklenmeye çalışılır.

```js
// kurali-dene.mjs — tasiyici paketi olmadan kural modulu yuklenebiliyor mu
const kok = process.argv[2];
try {
  const m = await import(`./${kok}/kural/ucret.mjs`);
  console.log(`${kok.padEnd(16)} -> yuklendi, disa verilen: ${Object.keys(m).join(", ")}`);
} catch (e) {
  console.log(`${kok.padEnd(16)} -> yuklenemedi, ${e.code}`);
}
```

```sh
for k in dogrudan arada tersine; do
  mkdir -p "$k-yalniz"
  cp -r "$k/kural" "$k-yalniz/kural"
  node kurali-dene.mjs "$k-yalniz"
done
```

```
dogrudan-yalniz  -> yuklenemedi, ERR_MODULE_NOT_FOUND
arada-yalniz     -> yuklenemedi, ERR_MODULE_NOT_FOUND
tersine-yalniz   -> yuklendi, disa verilen: ucret
```

Sözleşmesi olan ikinci sürüm de yüklenemedi. Kuralın gerçekleştirimden bağımsız
çözümlenebilmesi, sözleşmenin varlığına değil, sözleşmenin kural tarafında tanımlanmış
olmasına bağlı. Ölçünün sayı karşılığı budur: kuraldan gerçekleştirime giden kenar sayısı
sıfır olmadıkça kural tek başına ayakta duramaz.

## İlkenin Sınırı ve Bedeli

Üçüncü sürümde ücret modülü hangi taşıyıcıyla çalışacağını bilmiyor; taşıyıcı ona dışarıdan
veriliyor. Bu, Frontend Mimarisi ve Birim Testi kurslarında kurulan **bağımlılık
enjeksiyonu**dur ve burada ilkenin bir sonucu olarak beliriyor: bağımlılığını kendisi
kurmayan bir modülün onu bir yerden alması gerekir. O yer bileşim köküdür.

Ters çevirmenin bedeli de sayılabilir: üçüncü sürümde iki paket arasında bir sözleşme dosyası
fazladan duruyor ve taşıyıcı seçimi artık ücret modülünün içinde değil, dışında. Gerçekleştirimin
tek olduğu ve değişmeyeceği bilinen bir yerde bu fazlalık karşılıksızdır. İlke her bağımlılığın
tersine çevrilmesini istemez; değişme hızı yüksek olan tarafın, değişme hızı düşük olan tarafa
bağlanmasını ister. Taşıyıcı listesi ücret kuralından daha sık değişiyorsa yön tersine
çevrilir; tersi doğruysa ölçüm de tersini söyler.

## Özet

- Bağımlılığın tersine çevrilmesi, üst düzey kuralın gerçekleştirime değil soyutlamaya
  bağlanmasını ve soyutlamanın kural tarafından tanımlanmasını ister.
- Ölçü, paket sınırını geçen ithal kenarlarının yönüdür: kuraldan taşıyıcıya 2, 1 ve 0 kenar
  sayıldı; taşıyıcıdan kurala 0, 0 ve 2 kenar sayıldı.
- Soyutlamanın varlığı yetmez: sözleşmesi taşıyıcı paketinde duran sürümde giren kenar sayısı
  3 olmasına rağmen kural hâlâ taşıyıcı paketini ithal ediyordu.
- Kenar yönünün karşılığı koşumla görüldü: taşıyıcı paketi olmadan yalnız üçüncü sürümün kural
  modülü yüklendi, ilk ikisi `ERR_MODULE_NOT_FOUND` verdi.
- İlke her bağımlılığı değil, değişme hızı yüksek tarafa bakan bağımlılıkları çevirir;
  sözleşme dosyası ve dışarıdan verme düzeneği bunun bedelidir.

## Sonraki Adım

Beş ilke birimin **kime** bağlanacağını söyledi: kaç aktöre, hangi genişleme eksenine, hangi
sözleşmeye, hangi büyüklükteki arayüze, hangi taraftaki soyutlamaya. Hiçbiri bağın kendisini
bir ölçüye bağlamadı. İki modül arasındaki bağın gücü nasıl sayılır — paylaşılan ad sayısı
mı, geçilen parametre sayısı mı, ortak değiştirilen durum mu? Bir modülün içindeki parçaların
birbirine gerçekten ait olup olmadığı nasıl anlaşılır? Sonraki konu bu iki ölçüyü kurar ve
ilkelerin buraya kadar niteliksel bıraktığı yargıları sayıya çevirir.
