---
title: 'Her Yerde Aynı Dil'
source: 'https://academia.sh/tr/kurslar/alan-odakli-tasarim/her-yerde-ayni-dil'
course: 'Alan Odaklı Tasarım'
language: tr
updated: '2026-08-17T18:10:41+00:00'
license: 'CC BY-SA 4.0'
---

# Her Yerde Aynı Dil

Kod ile konuşma dilinin tek sözlükte birleşmesi: beş alan kavramına düşen ad sayısının iki sürümde sayılması, aynı kavramın iki adı arasında çeviri yapan nokta ve birim çevrimi sayısının ölçülmesi, aynı kuralın kaç gövdede yazıldığının ve tavanı kaç gövdenin uyguladığının çıkarılması, otuz gönderide teklif ile ücretin ayrılma sayısının ölçülmesi.

Önceki ders hizayı tek dosyada ölçtü: uzmanın on iki adı bir kez yazıldı ve on ikisi de
kodda bulundu. Gerçek bir kod tabanında aynı kavram birden çok yerde adlandırılır. Girdiyi
alan modül bir ad kullanır, ücret hesaplayan modül ikincisini, teklif üreten modül
üçüncüsünü. O zaman soru adların alandan gelip gelmediği değil, **kaç tanesinin** aynı şeyi
adlandırdığıdır.

Bu dersin konusu **her yerde aynı dil** (ubiquitous language): bir alan kavramının, alanın
konuşulduğu her yerde ve kodun her katmanında tek bir adla anılması. "Her yerde" sözü bir
üslup önerisi değil, ölçülebilir bir kısıttır — kavram başına ad sayısı bir olacaktır.

## Beş Kavram, On Üç Ad

Kitaplığın ücretlendirme tarafında beş kavram var: gönderi, ağırlık, bölge, indirim, asgari
ücret. Aşağıdaki sürüm üç dosyaya yayılmış ve her dosya kendi adlarını seçmiş.

```sh
mkdir -p dagnik birlesik
```

```js
// dagnik/giris.mjs — kaydin girisi: kargo
export const kargoKaydi = (agirlikGram, zonKodu, iskontolar) =>
  ({ agirlikGram, zonKodu, iskontolar });
```

```js
// dagnik/ucretlendirme.mjs — paket adiyla calisir, tavani uygular
const KADEME = [[1, 3900], [5, 6400], [Infinity, 11800]];
const KATSAYI = { "34": 1, "06": 1.35, "65": 1.8 };
const TABAN_FIYAT = 3990;
const TAVAN = 0.4;

export function ucret(kargo) {
  const paket = {
    agirlik: kargo.agirlikGram / 1000,
    bolge: kargo.zonKodu,
    indirimler: kargo.iskontolar,
  };
  const ham = KADEME.find(([u]) => paket.agirlik <= u)[1] * (KATSAYI[paket.bolge] ?? 1.8);
  const oran = Math.min(paket.indirimler.reduce((t, i) => t + i.oran, 0), TAVAN);
  return Math.max(Math.round(ham * (1 - oran)), TABAN_FIYAT);
}
```

```js
// dagnik/teklif.mjs — gonderi adiyla calisir, tavani bilmiyor
const KADEME = [[1, 3900], [5, 6400], [Infinity, 11800]];
const KATSAYI = { "34": 1, "06": 1.35, "65": 1.8 };
const ASGARI_UCRET = 3990;

export function teklif(kargo) {
  const gonderi = {
    kg: kargo.agirlikGram / 1000,
    bolgeKodu: kargo.zonKodu,
    iskontolar: kargo.iskontolar,
  };
  const ham = KADEME.find(([u]) => gonderi.kg <= u)[1] * (KATSAYI[gonderi.bolgeKodu] ?? 1.8);
  const oran = gonderi.iskontolar.reduce((t, i) => t + i.oran, 0);
  return Math.max(Math.round(ham * (1 - oran)), ASGARI_UCRET);
}
```

Üç dosyanın her biri kendi içinde tutarlıdır ve her adı alandan gelmiştir: `kargo`, `paket`,
`gonderi` sözcüklerinin üçü de kitaplığın konuşulduğu odada duyulur. Önceki dersin ad hizası
ölçüsü bu sürümde yüksek çıkar. Sorun tek tek adlarda değil, aynı kavramın birden çok adı
olmasındadır.

## Tek Sözlük

Birleşik sürüm her kavram için bir ad seçer ve o adı girişten hesaba kadar taşır. Ağırlık
kilogram cinsinden girer, dolayısıyla dosya içinde bir birim çevrimi kalmaz.

```js
// birlesik/giris.mjs — kaydin girisi: gonderi, agirlik kilogram cinsinden
export const gonderi = (agirlik, bolge, indirimler) => ({ agirlik, bolge, indirimler });
```

```js
// birlesik/ucretlendirme.mjs — tek ad kumesi, tek kural govdesi
const KADEMELER = [[1, 3900], [5, 6400], [Infinity, 11800]];
const BOLGE_KATSAYISI = { "34": 1, "06": 1.35, "65": 1.8 };
const ASGARI_UCRET = 3990;
const INDIRIM_TAVANI = 0.4;

const tabanUcret = (agirlik) => KADEMELER.find(([ust]) => agirlik <= ust)[1];
const indirimOrani = (indirimler) =>
  Math.min(indirimler.reduce((t, i) => t + i.oran, 0), INDIRIM_TAVANI);

function netUcret(gonderi) {
  const ham = tabanUcret(gonderi.agirlik) * (BOLGE_KATSAYISI[gonderi.bolge] ?? 1.8);
  return Math.max(Math.round(ham * (1 - indirimOrani(gonderi.indirimler))), ASGARI_UCRET);
}

export const ucret = (gonderi) => netUcret(gonderi);
export const teklif = (gonderi) => netUcret(gonderi);
```

Alanda teklif ile fatura ücretinin aynı kurala tabi olduğu söyleniyor; birleşik sürümde bu
cümlenin karşılığı iki dışa açılan adın aynı gövdeyi çağırmasıdır.

## Ölçüm

Dört ölçü alınır: kavram başına ad sayısı, aynı kavramın iki adı arasında çeviri yapan nokta
sayısı, kuralın kaç gövdede yazıldığı ve tavanı kaç gövdenin uyguladığı. Sonuna otuz
gönderide teklif ile ücretin ayrılma sayısı eklenir.

```js
// dil-olc.mjs — kavram basina ad, ceviri noktasi, kural govdesi ve iki yolun ayrilmasi
import { readFileSync } from "node:fs";
import { ucret as dUcret } from "./dagnik/ucretlendirme.mjs";
import { teklif as dTeklif } from "./dagnik/teklif.mjs";
import { kargoKaydi } from "./dagnik/giris.mjs";
import { ucret as bUcret, teklif as bTeklif } from "./birlesik/ucretlendirme.mjs";
import { gonderi as bGonderi } from "./birlesik/giris.mjs";

const KAVRAM = {
  gonderi: ["kargo", "paket", "gonderi"],
  agirlik: ["agirlikGram", "kg", "agirlik"],
  bolge: ["zonKodu", "bolgeKodu", "bolge"],
  indirim: ["iskontolar", "indirimler"],
  asgariUcret: ["TABAN_FIYAT", "ASGARI_UCRET"],
};
const SURUM = {
  dagnik: ["dagnik/giris.mjs", "dagnik/ucretlendirme.mjs", "dagnik/teklif.mjs"],
  birlesik: ["birlesik/giris.mjs", "birlesik/ucretlendirme.mjs"],
};
const kavramiBul = (ad) => Object.keys(KAVRAM).find((k) => KAVRAM[k].includes(ad)) ?? null;

for (const [surum, dosyalar] of Object.entries(SURUM)) {
  const metin = dosyalar.map((d) => readFileSync(d, "utf8")).join("\n");
  const jeton = new Set(metin.match(/[A-Za-z_][A-Za-z0-9_]*/g) ?? []);
  const dokum = [];
  let toplamAd = 0;
  for (const [kavram, adlar] of Object.entries(KAVRAM)) {
    const varOlan = adlar.filter((a) => jeton.has(a));
    toplamAd += varOlan.length;
    dokum.push(`${kavram}=${varOlan.length}`);
  }
  const tek = metin.replace(/\s+/g, " ");
  const ceviri = [...tek.matchAll(/(\w+):\s*\w+\.(\w+)/g)].filter(
    ([, sol, sag]) => sol !== sag && kavramiBul(sol) && kavramiBul(sol) === kavramiBul(sag));
  const birim = [...tek.matchAll(/\/ 1000/g)].length;
  const govdeli = dosyalar.map((d) => readFileSync(d, "utf8"))
    .filter((m) => /Math\.max\(Math\.round/.test(m));
  const tavanli = govdeli.filter((m) => /TAVAN/.test(m)).length;
  console.log(`${surum}: dosya ${dosyalar.length}, 5 kavrama ${toplamAd} ad`);
  console.log(`  ad dagilimi: ${dokum.join("  ")}`);
  console.log(`  ceviri noktasi ${ceviri.length}, birim cevrimi ${birim}`);
  console.log(`  kural govdesi ${govdeli.length}, tavani uygulayan govde ${tavanli}`);
}

const ORNEK = [];
for (let i = 0; i < 30; i += 1) {
  const hacimOrani = [0.1, 0.2, 0.3][i % 3] + (i % 5) * 0.05;
  ORNEK.push([600 + i * 400, ["34", "06", "65"][i % 3],
    [{ ad: "sozlesme", oran: 0.2 }, { ad: "hacim", oran: Math.round(hacimOrani * 100) / 100 }]]);
}
const ayrilan = (a, b) => ORNEK.filter((o) => a(o) !== b(o)).length;

console.log(`dagnik   ucret != teklif : ${ayrilan(
  (o) => dUcret(kargoKaydi(...o)), (o) => dTeklif(kargoKaydi(...o)))} / ${ORNEK.length}`);
console.log(`birlesik ucret != teklif : ${ayrilan(
  (o) => bUcret(bGonderi(o[0] / 1000, o[1], o[2])),
  (o) => bTeklif(bGonderi(o[0] / 1000, o[1], o[2])))} / ${ORNEK.length}`);
```

```sh
node dil-olc.mjs
```

```
dagnik: dosya 3, 5 kavrama 13 ad
  ad dagilimi: gonderi=3  agirlik=3  bolge=3  indirim=2  asgariUcret=2
  ceviri noktasi 5, birim cevrimi 2
  kural govdesi 2, tavani uygulayan govde 1
birlesik: dosya 2, 5 kavrama 5 ad
  ad dagilimi: gonderi=1  agirlik=1  bolge=1  indirim=1  asgariUcret=1
  ceviri noktasi 0, birim cevrimi 0
  kural govdesi 1, tavani uygulayan govde 1
dagnik   ucret != teklif : 19 / 30
birlesik ucret != teklif : 0 / 30
```

Beş kavram dağınık sürümde on üç ad taşıyor, birleşik sürümde beş. Aradaki sekiz fazla adın
bedeli ikinci satırda: aynı kavramın iki adı arasında beş çeviri noktası ve iki birim
çevrimi. Çeviri noktası, bir dosyanın öteki dosyanın sözlüğünü bilmek zorunda olduğu
yerdir; her biri bir eşleme satırı olarak yazılır ve bir ad değiştiğinde tek tek gözden
geçirilir.

## Ayrılan Sonuçların Kaynağı

Son iki satır hizasızlığın bedelini davranış cinsinden veriyor: otuz gönderinin
on dokuzunda teklif ile ücret farklı çıkıyor, oysa alan uzmanı ikisinin aynı olduğunu
söylüyor. Ayrılmanın doğrudan yapısal nedeni dördüncü satırdadır: dağınık sürümde iki kural
gövdesi var ve tavanı yalnız biri uyguluyor.

Buradaki asıl gözlem, iki gövdenin *aynı kural olduğunun kod üzerinden görünmemesidir*.
`paket.indirimler` ile `gonderi.iskontolar` iki ayrı ad olduğu için tavanı arayan bir okuma
ikinci gövdede aranacak sözcüğü bulamaz. Adlar ayrıldığında kuralların ayrılması bir dikkat
kusuru olmaktan çıkar, yapının izin verdiği bir sonuç hâline gelir. Birleşik sürümde aynı
ayrılma yazılamaz: tek gövde vardır, iki dışa açılan ad onu çağırır ve ayrılan sonuç 0'a
iner.

## Sözlüğün Bakımı

Tek sözlük bir kereye mahsus bir seçim değildir; alan uzmanının dili değiştikçe kodun da
değişmesi gerekir. Bunun iki yönü vardır. Birincisi, kodda bir ad değiştiğinde alanda
karşılığının da değişmiş olması: `iskonto` sözcüğü artık kullanılmıyorsa kaynakta da
kalmamalıdır. İkincisi ve daha önemlisi, alanda **yeni bir ayrım** doğduğunda kodda yeni bir
ad açılmasıdır. Sözleşmeli müşterinin indirimi ile kampanya indirimi ayrı kurallara tabi
olmaya başladığı anda, ikisini `indirim` adı altında toplamak tek sözlük olmaktan çıkar —
alanda iki kavram olan şeyi kodda bir kavramla anmak, aynı hizasızlığın ters yöndeki
biçimidir.

Bu yüzden ölçü çift taraflıdır: kavram başına ad sayısı bir olacak, ad başına kavram sayısı
da bir olacaktır. İkinci kısmın ihlali dersin sonunda görünmeye başlar; aynı `gonderi`
sözcüğünün iki farklı şeyi adlandırdığı durum bu konunun ölçtüğü şeyin dışındadır.

## Ne Zaman Karşılığını Vermez

Tek sözlüğün bedeli yeniden adlandırma işidir ve bu bedel dokunulan dosya sayısıyla ölçülür.
Yukarıdaki üç dosyada sekiz fazla adı gidermek küçük bir iştir; sözlüğün yüz dosyaya yayıldığı
bir kod tabanında aynı iş bir dizi mekanik değişiklik gerektirir ve Yazılım Kalitesi ve Test
kursunda kurulan davranış koruması olmadan yapılamaz.

İkinci durum ölçünün girdisiyle ilgilidir. Ad sayısı ancak kavram listesi sabitse
sayılabilir. Alanın kendisi henüz oturmamışsa — hangi ayrımların kalıcı olduğu bilinmiyorsa —
adlar da oturmaz ve erken yapılan bir birleştirme, alanda gerçekten iki olan şeyi tek ada
sıkıştırabilir. Bu durumda ölçü sıfıra iner ama hiza bozulur.

## Özet

- Her yerde aynı dil, bir alan kavramının kodun her yerinde tek adla anılmasıdır; ölçüsü
  kavram başına ad sayısıdır.
- Dağınık sürümde beş kavram on üç ad taşıyordu; birleşik sürümde beş ad kaldı.
- Fazla adların bedeli sayıldı: aynı kavramın iki adı arasında 5 çeviri noktası ve 2 birim
  çevrimi, birleşik sürümde 0 ve 0.
- Dağınık sürümde kural iki gövdeye bölünmüştü ve tavanı yalnız biri uyguluyordu; otuz
  gönderinin 19'unda teklif ile ücret ayrıldı, birleşik sürümde 0'ında.
- Ölçü çift taraflıdır: kavram başına bir ad, ad başına bir kavram. İkincisinin ihlali
  alanda iki olan şeyi kodda bir adla anmaktır.

## Sonraki Adım

Birleşik sürümde gönderi üç alan taşıyan düz bir kayıttı ve iki gönderi ancak alanları
eşitse aynı sayılabilirdi. Alanda bu doğru değildir: aynı gönderinin ağırlığı tartım
düzeltmesiyle değişir ve gönderi hâlâ aynı gönderidir; iki ayrı gönderinin bütün alanları
tesadüfen eşit olabilir ve ikisi hâlâ iki gönderidir. Sonraki ders bu ayrımı ölçüye çevirir:
aynı kimliğin kaç ayrı örnekle temsil edilebildiğini ve alanları eşitleyince kaç ayrı
gönderinin tek örneğe çöktüğünü sayar, kimliği açıkça taşıyan bir modelle karşılaştırır.
