---
title: 'Kodun Aktöre Göre Düzenlenmesi'
source: 'https://academia.sh/tr/kurslar/temiz-kod/kodun-aktore-gore-duzenlenmesi'
course: 'Temiz Kod'
language: tr
updated: '2026-08-17T18:10:37+00:00'
license: 'CC BY-SA 4.0'
---

# Kodun Aktöre Göre Düzenlenmesi

Kodun teknik katmana göre değil değişim nedenine göre gruplanmasının ölçülmesi: aynı ücret paketinin iki düzeninde bir dosyada kaç aktörün kararının tanımlı olduğu, her aktörün kaç dosyaya yayıldığı ve tek bir muhasebe değişikliğinin iki düzende kaç dosyaya dokunduğu.

Görünürlük kararı bir modülün dışarıya ne verdiğini belirliyor. Bir soru daha kalıyor: bu
modüller hangi ölçüte göre ayrıldı? Ücret hesabı bir dosyada, biçimlendirme başka bir
dosyada; ayrım teknik görünüyor ve makul.

Bir kod tabanını asıl zorlayan şey, farklı kişilerin farklı nedenlerle aynı dosyaya
dokunmasıdır. Muhasebenin istediği bir yuvarlama değişikliği ile operasyonun istediği bir
kademe değişikliği aynı dosyada buluşuyorsa, iki karar birbirini bekler; biri diğerinin
sınamasını bozar. Bu ders, bir dosyanın kaç ayrı **aktör**e hizmet ettiğini sayıyor.

## Aktör Nedir

Aktör, bir değişiklik isteğinin kaynağıdır: bir rol, bir birim, bir paydaş. Ücretlendirme
kitaplığında üçü var.

- **Operasyon**, taşımanın nasıl yapıldığına karar verir: ağırlık kademeleri, hacimsel
  bölen, aktarma sayısı, teslim süresi.
- **Muhasebe**, paranın nasıl sayıldığına karar verir: vergi oranı, yuvarlama kuralı,
  fatura satırının biçimi.
- **Müşteri iletişimi**, müşteriye ne söylendiğine karar verir: etiket metni, teslim
  mesajı.

Üçü birbirinden bağımsız değişir. Vergi oranı değiştiğinde ağırlık kademelerinin
değişmesi için bir neden yoktur. Ölçüt buradan çıkar: bir dosya tek bir aktörün
isteğiyle değişmelidir.

## Katmana Göre Düzen

Birinci düzen dosyaları teknik işlevine göre ayırıyor: hesap, akış, biçim. Bu ayrım
Veri Erişim Katmanı ve İş Mantığı kursunda kurulan katman sorumluluklarına benzer
görünür, ama orada ayrımı belirleyen şey bağımlılık yönüydü; burada yalnız işin türü.

```sh
mkdir -p katman aktor
```

```js
// katman/hesap.mjs — operasyonun kademe kurallari ile muhasebenin vergi kurallari bir arada
export const HACIMSEL_BOLEN = 3000;
export const AGIRLIK_KADEMELERI = [{ ustSinirKg: 1, kiloBasiUcret: 38 },
  { ustSinirKg: 5, kiloBasiUcret: 26 }, { ustSinirKg: 30, kiloBasiUcret: 17 }];
export const KDV_ORANI = 0.2;

export function ucretlendirilenAgirlikKg(gonderi) {
  const hacimsel = (gonderi.enCm * gonderi.boyCm * gonderi.yukseklikCm) / HACIMSEL_BOLEN;
  return Math.max(gonderi.agirlikKg, hacimsel);
}

export function kademeSec(kg) {
  return AGIRLIK_KADEMELERI.find((k) => kg <= k.ustSinirKg);
}

export function netUcret(gonderi) {
  const kg = ucretlendirilenAgirlikKg(gonderi);
  return kademeSec(kg).kiloBasiUcret * kg;
}

export function yuvarla(tutar) {
  return Math.round(tutar * 100) / 100;
}

export function kdv(net) {
  return yuvarla(net * KDV_ORANI);
}
```

```js
// katman/akis.mjs — operasyonun aktarma kurali ile musterinin teslim mesaji bir arada
export function aktarmaSayisi(gonderi) {
  return gonderi.bolgeKodu > 2 ? 2 : 1;
}

export function teslimGunu(gonderi) {
  return 1 + aktarmaSayisi(gonderi);
}

export function teslimMesaji(gonderi) {
  return `${gonderi.kod} kargonuz ${teslimGunu(gonderi)} gun icinde teslim edilecek`;
}
```

```js
// katman/bicim.mjs — muhasebenin fatura bicimi ile musterinin etiket metni bir arada
import { netUcret, kdv, yuvarla, ucretlendirilenAgirlikKg } from "./hesap.mjs";
import { aktarmaSayisi } from "./akis.mjs";

export function faturaSatiri(gonderi) {
  const net = yuvarla(netUcret(gonderi));
  const vergi = kdv(netUcret(gonderi));
  return `${gonderi.kod} net=${net.toFixed(2)} kdv=${vergi.toFixed(2)} toplam=${yuvarla(net + vergi).toFixed(2)}`;
}

export function etiketMetni(gonderi) {
  return `${gonderi.kod} ${ucretlendirilenAgirlikKg(gonderi).toFixed(2)}kg ${aktarmaSayisi(gonderi)} aktarma`;
}
```

Her dosya kendi içinde tutarlı. Yine de `hesap.mjs` hem kademeleri hem vergiyi,
`bicim.mjs` hem faturayı hem etiketi, `akis.mjs` hem aktarmayı hem müşteri mesajını
taşıyor.

## Aktöre Göre Düzen

İkinci düzen aynı fonksiyonları aktörlerine göre topluyor.

```js
// aktor/operasyon.mjs — kademe, hacim ve aktarma kararlari
export const HACIMSEL_BOLEN = 3000;
export const AGIRLIK_KADEMELERI = [{ ustSinirKg: 1, kiloBasiUcret: 38 },
  { ustSinirKg: 5, kiloBasiUcret: 26 }, { ustSinirKg: 30, kiloBasiUcret: 17 }];

export function ucretlendirilenAgirlikKg(gonderi) {
  const hacimsel = (gonderi.enCm * gonderi.boyCm * gonderi.yukseklikCm) / HACIMSEL_BOLEN;
  return Math.max(gonderi.agirlikKg, hacimsel);
}

export function kademeSec(kg) {
  return AGIRLIK_KADEMELERI.find((k) => kg <= k.ustSinirKg);
}

export function netUcret(gonderi) {
  const kg = ucretlendirilenAgirlikKg(gonderi);
  return kademeSec(kg).kiloBasiUcret * kg;
}

export function aktarmaSayisi(gonderi) {
  return gonderi.bolgeKodu > 2 ? 2 : 1;
}

export function teslimGunu(gonderi) {
  return 1 + aktarmaSayisi(gonderi);
}
```

```js
// aktor/muhasebe.mjs — vergi, yuvarlama ve fatura bicimi
import { netUcret } from "./operasyon.mjs";

export const KDV_ORANI = 0.2;

export function yuvarla(tutar) {
  return Math.round(tutar * 100) / 100;
}

export function kdv(net) {
  return yuvarla(net * KDV_ORANI);
}

export function faturaSatiri(gonderi) {
  const net = yuvarla(netUcret(gonderi));
  const vergi = kdv(netUcret(gonderi));
  return `${gonderi.kod} net=${net.toFixed(2)} kdv=${vergi.toFixed(2)} toplam=${yuvarla(net + vergi).toFixed(2)}`;
}
```

```js
// aktor/musteri.mjs — musteriye gorunen metinler
import { ucretlendirilenAgirlikKg, aktarmaSayisi, teslimGunu } from "./operasyon.mjs";

export function etiketMetni(gonderi) {
  return `${gonderi.kod} ${ucretlendirilenAgirlikKg(gonderi).toFixed(2)}kg ${aktarmaSayisi(gonderi)} aktarma`;
}

export function teslimMesaji(gonderi) {
  return `${gonderi.kod} kargonuz ${teslimGunu(gonderi)} gun icinde teslim edilecek`;
}
```

```js
// karsilastir.mjs — iki duzenin ayni ciktiyi urettigi dogrulanir
import { faturaSatiri as kFatura, etiketMetni as kEtiket } from "./katman/bicim.mjs";
import { teslimMesaji as kMesaj } from "./katman/akis.mjs";
import { faturaSatiri as aFatura } from "./aktor/muhasebe.mjs";
import { etiketMetni as aEtiket, teslimMesaji as aMesaj } from "./aktor/musteri.mjs";

const gonderi = { kod: "GN-4172", agirlikKg: 2.4, enCm: 30, boyCm: 24, yukseklikCm: 18, bolgeKodu: 3 };
for (const [katman, aktor] of [[kFatura, aFatura], [kEtiket, aEtiket], [kMesaj, aMesaj]]) {
  const a = katman(gonderi), b = aktor(gonderi);
  console.log(`${a === b ? "ayni  " : "FARKLI"} ${a}`);
}
```

```
ayni   GN-4172 net=112.32 kdv=22.46 toplam=134.78
ayni   GN-4172 4.32kg 2 aktarma
ayni   GN-4172 kargonuz 3 gun icinde teslim edilecek
```

## Ölçüm: Bir Dosyada Kaç Aktör

Ölçer her aktörün sözcük dağarcığını alır ve her dosyada hangi aktörlerin kararlarının
**tanımlı** olduğuna bakar. İthal edilen bir ad o dosyanın kararı değildir; yalnız
tanımlar sayılır.

```js
// aktor-olc.mjs — her dosyada hangi aktorun kararlari tanimli, her aktor kac dosyaya yayilmis
import { readFileSync, readdirSync } from "node:fs";

const AKTOR = {
  operasyon: ["HACIMSEL_BOLEN", "AGIRLIK_KADEMELERI", "ucretlendirilenAgirlikKg", "kademeSec",
    "netUcret", "aktarmaSayisi", "teslimGunu"],
  muhasebe: ["KDV_ORANI", "yuvarla", "kdv", "faturaSatiri"],
  musteri: ["etiketMetni", "teslimMesaji"],
};

// Yalnizca tanimlar sayilir; baska bir dosyadan ithal edilen ad o dosyanin karari degildir.
const tanimlar = (m) => [...m.matchAll(/^export\s+(?:const|function|class)\s+(\w+)/gm)].map((e) => e[1]);

for (const dizin of process.argv.slice(2)) {
  const dosyalar = readdirSync(dizin).filter((a) => a.endsWith(".mjs")).sort();
  const yayilim = Object.fromEntries(Object.keys(AKTOR).map((a) => [a, 0]));
  let cakisan = 0;
  console.log(dizin);
  for (const dosya of dosyalar) {
    const adlar = new Set(tanimlar(readFileSync(`${dizin}/${dosya}`, "utf8")));
    const sahipler = Object.entries(AKTOR)
      .filter(([, sozcukler]) => sozcukler.some((s) => adlar.has(s))).map(([a]) => a);
    for (const a of sahipler) yayilim[a] += 1;
    if (sahipler.length > 1) cakisan += 1;
    console.log(`  ${dosya.padEnd(14)} aktor=${sahipler.length}  [${sahipler.join(", ")}]`);
  }
  const dokum = Object.entries(yayilim).map(([a, n]) => `${a}=${n}`).join("  ");
  console.log(`  cakisan dosya=${cakisan}   aktor basina dosya: ${dokum}`);
}
```

```sh
node aktor-olc.mjs katman aktor
```

```
katman
  akis.mjs       aktor=2  [operasyon, musteri]
  bicim.mjs      aktor=2  [muhasebe, musteri]
  hesap.mjs      aktor=2  [operasyon, muhasebe]
  cakisan dosya=3   aktor basina dosya: operasyon=2  muhasebe=2  musteri=2
aktor
  muhasebe.mjs   aktor=1  [muhasebe]
  musteri.mjs    aktor=1  [musteri]
  operasyon.mjs  aktor=1  [operasyon]
  cakisan dosya=0   aktor basina dosya: operasyon=1  muhasebe=1  musteri=1
```

Katman düzeninde üç dosyanın üçü de çakışma noktası; her biri iki ayrı aktörün isteğiyle
değişebilir. Aktör düzeninde çakışma yok. İkinci sayı da aynı şeyi tersinden söylüyor:
katman düzeninde her aktörün kararları iki dosyaya yayılmış, aktör düzeninde bir dosyada
toplanmış.

## Gerçek Bir Değişiklik

Sayıların iddiası bir istekle sınanır. Muhasebe iki şey değiştiriyor: katma değer vergisi
oranı yüzde yirmiden yüzde ona iniyor ve tutarlar kuruşa değil tam liraya yuvarlanıyor.
İkinci karar fatura satırının biçimini de etkiliyor, çünkü kuruş hanesi artık anlamsız.

```sh
cp -r katman katman-yeni && cp -r aktor aktor-yeni
```

```js
// katman-yeni/hesap.mjs — vergi orani ve yuvarlama kurali degisti
export const HACIMSEL_BOLEN = 3000;
export const AGIRLIK_KADEMELERI = [{ ustSinirKg: 1, kiloBasiUcret: 38 },
  { ustSinirKg: 5, kiloBasiUcret: 26 }, { ustSinirKg: 30, kiloBasiUcret: 17 }];
export const KDV_ORANI = 0.1;

export function ucretlendirilenAgirlikKg(gonderi) {
  const hacimsel = (gonderi.enCm * gonderi.boyCm * gonderi.yukseklikCm) / HACIMSEL_BOLEN;
  return Math.max(gonderi.agirlikKg, hacimsel);
}

export function kademeSec(kg) {
  return AGIRLIK_KADEMELERI.find((k) => kg <= k.ustSinirKg);
}

export function netUcret(gonderi) {
  const kg = ucretlendirilenAgirlikKg(gonderi);
  return kademeSec(kg).kiloBasiUcret * kg;
}

export function yuvarla(tutar) {
  return Math.round(tutar);
}

export function kdv(net) {
  return yuvarla(net * KDV_ORANI);
}
```

```js
// katman-yeni/bicim.mjs — fatura satirindan kurus hanesi kalkti
import { netUcret, kdv, yuvarla, ucretlendirilenAgirlikKg } from "./hesap.mjs";
import { aktarmaSayisi } from "./akis.mjs";

export function faturaSatiri(gonderi) {
  const net = yuvarla(netUcret(gonderi));
  const vergi = kdv(netUcret(gonderi));
  return `${gonderi.kod} net=${net.toFixed(0)} kdv=${vergi.toFixed(0)} toplam=${yuvarla(net + vergi).toFixed(0)}`;
}

export function etiketMetni(gonderi) {
  return `${gonderi.kod} ${ucretlendirilenAgirlikKg(gonderi).toFixed(2)}kg ${aktarmaSayisi(gonderi)} aktarma`;
}
```

```js
// aktor-yeni/muhasebe.mjs — ayni iki karar, tek dosyada
import { netUcret } from "./operasyon.mjs";

export const KDV_ORANI = 0.1;

export function yuvarla(tutar) {
  return Math.round(tutar);
}

export function kdv(net) {
  return yuvarla(net * KDV_ORANI);
}

export function faturaSatiri(gonderi) {
  const net = yuvarla(netUcret(gonderi));
  const vergi = kdv(netUcret(gonderi));
  return `${gonderi.kod} net=${net.toFixed(0)} kdv=${vergi.toFixed(0)} toplam=${yuvarla(net + vergi).toFixed(0)}`;
}
```

```sh
for d in katman aktor; do
  n=0
  for f in "$d"/*.mjs; do
    diff -q <(tail -n +2 "$f") <(tail -n +2 "$d-yeni/$(basename "$f")") > /dev/null || n=$((n + 1))
  done
  echo "$d duzeni: muhasebe degisikligi $n dosyaya dokundu"
done
```

```
katman duzeni: muhasebe degisikligi 2 dosyaya dokundu
aktor duzeni: muhasebe degisikligi 1 dosyaya dokundu
```

Katman düzeninde `bicim.mjs` de değişti ve o dosyada müşteri iletişiminin sahip olduğu
`etiketMetni` de duruyor. Muhasebenin isteğiyle açılan bir değişiklik, müşteri
iletişiminin dosyasına dokunmuş oldu. Aktör düzeninde `musteri.mjs` hiç açılmadı.

Etiket metnindeki ağırlığın iki haneli kalması da bunu gösteriyor: kuruş kararı paraya
aitti, ağırlığa değil. Katman düzeninde iki biçim aynı dosyada durduğu için ayrımı yapan
şey dosya değil, değişikliği yapan kişinin dikkatidir.

## Ayrımın Sınırı

Aktöre göre gruplama, teknik ayrımların yerine geçmez; onlarla dik açıda durur. Bir
uygulamada hem katman ayrımı (bağımlılık yönünü belirler) hem aktör ayrımı (değişim
nedenini belirler) bulunur. İkisi çakıştığında seçim, hangi ayrımın daha sık değiştiğine
göre yapılır: yılda üç kez vergi değişiyor ve yılda bir kez veri kaynağı değişiyorsa,
üstteki bölme aktöre göre olur.

İkinci sınır, aktörlerin de zamanla değişmesidir. Muhasebe ile fiyatlandırma bugün aynı
birimse `muhasebe.mjs` ikisini birden taşır; ayrıldıkları gün dosya da bölünür. Ölçüm bu
durumu erken gösterir: bir dosyanın aktör sayısı ikiye çıktığında bölme zamanı gelmiştir.

Üçüncü sınır, ölçünün sözlüğe bağlı olmasıdır. Aktör sözlüğü elle yazılır ve yanlış
yazılırsa ölçüm de yanlış çıkar. Sözlüğü doğru tutan şey koddan değil, değişiklik
isteklerinin geldiği yerden okunur.

## Özet

- Aktör, bir değişiklik isteğinin kaynağıdır; ölçüt, bir dosyanın tek bir aktörün
  isteğiyle değişmesidir.
- Katman düzeninde üç dosyanın üçü de iki aktörün kararını taşıyordu; aktör düzeninde
  çakışan dosya sayısı sıfır.
- Aktör başına dosya sayısı ikiden bire indi; her aktörün kararları tek bir yerde toplandı.
- Vergi oranı ve yuvarlama değişikliği katman düzeninde iki, aktör düzeninde bir dosyaya
  dokundu; katman düzeninde dokunulan ikinci dosya müşteri iletişiminin metnini de
  taşıyordu.
- Aktör ayrımı katman ayrımının yerine geçmez; ikisi çakıştığında daha sık değişen ayrım
  üstte durur ve bir dosyanın aktör sayısı ikiye çıktığında bölme zamanı gelmiştir.

## Sonraki Adım

Aktör düzeninde `faturaSatiri` bir dosyaya taşındı, ama kitaplıkta hâlâ aynı işin birden
çok biçimde yapıldığı yerler var: bir yerde tutar `toFixed` ile, başka bir yerde elle
biçimlendiriliyor; bir yerde hata atılıyor, başka bir yerde boş sonuç dönüyor. Bunların
hiçbiri yanlış değil, yalnız farklı. Farklılığın da bir bedeli var ve sayılabilir.
Sonraki ders aynı işin kaç ayrı biçimde yapıldığını bulan bir tarama yazar, tekilleştirme
sonrası ölçümü tekrarlar.
