---
title: 'Yozlaşma Önleyici Katman'
source: 'https://academia.sh/tr/kurslar/alan-odakli-tasarim/yozlasma-onleyici-katman'
course: 'Alan Odaklı Tasarım'
language: tr
updated: '2026-08-17T18:10:41+00:00'
license: 'CC BY-SA 4.0'
---

# Yozlaşma Önleyici Katman

Dış modelin sızmasının engellenmesi: iki taşıyıcı sağlayıcısının ad, durum kümesi ve birimlerinin kaç dosyada göründüğünün sayılması, iş kuralının tanıdığı dış durum değeri sayısının ölçülmesi, çeviri katmanı eklendiğinde çekirdek dosyalardaki sızmanın sıfıra inmesi ve katmanın dosya ile satır cinsinden bedelinin çıkarılması.

Bir önceki dersin çeviri dosyaları elverişli bir durumda çalıştı: yukarı akıştaki sözleşme
bağlamı aynı kod tabanının parçasıydı, adları düzenliydi, tipleri belliydi. Kitaplığın
konuştuğu yukarı akışların bir kısmı böyle değil. Taşıyıcı sağlayıcılarının kendi modelleri,
kendi durum kümeleri, kendi kimlik biçimleri ve kendi ağırlık birimleri var; hiçbiri
kitaplığın alan diline uymak zorunda değil ve değiştiklerinde kimseye danışmıyorlar.

Bu dersin sorusu arayüz sorusu değil. İki sağlayıcının çağrı biçimlerinin farklı olması,
Tasarım İlkeleri ve Tasarım Kalıpları kurslarında ölçülmüş bir sorundur ve çözümü
bağdaştırıcıdır. Buradaki sorun bir düzey içeride: sağlayıcının **kavramları** içeri sızarsa iç
model dışın diliyle konuşmaya başlar. Teslimat bağlamının durumları taşıyıcının hareket
kodlarına dönüşür, gönderinin ağırlığı taşıyıcının birimine bağlanır, "kayıt bulunamadı" gibi
bir aktarım hatası alanın bir durumu gibi davranır. Bu ders o sızmayı sayar.

## Uyumsuzluğun İki Düzeyi

Bağdaştırıcı, iki tarafın **imzalarını** uzlaştırır: çağrının adı, parametre sırası, dönüş
biçimi. Uzlaştırdığı şey biçimdir, kavram değildir. Bir bağdaştırıcı iki sağlayıcıyı tek bir
işlev adı altında toplayabilir ve yine de her ikisinin durum kodlarını olduğu gibi yukarıya
verebilir.

**Yozlaşma önleyici katman** (anti-corruption layer) ise iki tarafın **modellerini**
uzlaştırır: dışın durum kümesi içinkine, dışın birimi içinkine, dışın hata biçimi içinkine
çevrilir ve bu çeviri tek bir yerde durur. Adı bunu söylüyor: korunan şey iç modelin
kendisidir. Ölçüsü de buradan çıkar — dışın adları ve değerleri iç modelin dosyalarında kaç
kez görünüyor?

İki sağlayıcı, kitaplığın dışında duran iki dış sistem olarak yazılıyor. Kendi sözlükleri
kasten birbirine benzemiyor.

```sh
mkdir -p saglayici sizan korumali
```

```js
// saglayici/tasiyici-a.mjs — dis sistem A: kendi hareket kodlari, epok zamani, libre agirligi
const KAYITLAR = {
  "AA-1041": { hareketKodu: "H25", sonHareketEpok: 1700000000, agirlikLibre: 5.3, hataKodu: 0 },
  "AA-1042": { hareketKodu: "H30", sonHareketEpok: 1700050000, agirlikLibre: 1.5, hataKodu: 0 },
};

export function sevkDurumu(sevkKimligi) {
  const k = KAYITLAR[sevkKimligi];
  return k === undefined
    ? { sevkKimligi, hareketKodu: "H99", sonHareketEpok: 0, agirlikLibre: 0, hataKodu: 404 }
    : { sevkKimligi, ...k };
}
```

```js
// saglayici/tasiyici-b.mjs — dis sistem B: kendi durum etiketleri, ISO zamani, ons agirligi
const KAYITLAR = {
  "BB/2210": { durumEtiketi: "tamamlandi", sonHareketZamani: "2023-11-14T22:13:20Z", agirlikOns: 84 },
  "BB/2211": { durumEtiketi: "yolda", sonHareketZamani: "2023-11-15T20:00:00Z", agirlikOns: 12 },
};

export function takip(referans) {
  const k = KAYITLAR[referans];
  return k === undefined
    ? { referans, durumEtiketi: "kayit-yok", sonHareketZamani: null, agirlikOns: 0 }
    : { referans, ...k };
}
```

Farklar sayılabilir: A durumu altı kodla, B beş etiketle veriyor; A zamanı epok saniyesi, B
ISO metni olarak veriyor; A ağırlığı libre, B ons cinsinden veriyor; A bulunamayan kaydı hata
koduyla, B bir durum etiketiyle bildiriyor. Alan tarafındaki iş kuralı ise tek bir soru
soruyor: bu gönderi gecikmeli mi?

## Sızan Düzen

Birinci düzende iş kuralı iki sağlayıcının sözlüğünü doğrudan tanıyor.

```js
// sizan/gecikme.mjs — is kurali iki saglayicinin durum sozlugunu ve birimlerini dogrudan taniyor
import { sevkDurumu } from "../saglayici/tasiyici-a.mjs";
import { takip } from "../saglayici/tasiyici-b.mjs";

export function gecikmeliMi(tasiyici, referans, pencereSaat, simdiEpok) {
  if (tasiyici === "A") {
    const y = sevkDurumu(referans);
    if (y.hataKodu !== 0 || y.hareketKodu === "H99") return null;
    if (y.hareketKodu === "H30" || y.hareketKodu === "H40") return false;
    return (simdiEpok - y.sonHareketEpok) / 3600 > pencereSaat;
  }
  const y = takip(referans);
  if (y.durumEtiketi === "kayit-yok") return null;
  if (y.durumEtiketi === "tamamlandi" || y.durumEtiketi === "iade") return false;
  return (simdiEpok - Math.floor(Date.parse(y.sonHareketZamani) / 1000)) / 3600 > pencereSaat;
}
```

```js
// sizan/rapor.mjs — rapor da iki saglayicinin birim ve durum sozluguyle yaziliyor
import { sevkDurumu } from "../saglayici/tasiyici-a.mjs";
import { takip } from "../saglayici/tasiyici-b.mjs";
import { gecikmeliMi } from "./gecikme.mjs";

export function ozet(kayitlar, pencereSaat, simdiEpok) {
  let gecikmeli = 0, bilinmeyen = 0, gram = 0;
  for (const [tasiyici, referans] of kayitlar) {
    const g = gecikmeliMi(tasiyici, referans, pencereSaat, simdiEpok);
    if (g === null) bilinmeyen += 1; else if (g) gecikmeli += 1;
    gram += tasiyici === "A"
      ? Math.round(sevkDurumu(referans).agirlikLibre * 453.59237)
      : Math.round(takip(referans).agirlikOns * 28.349523125);
  }
  return { gecikmeli, bilinmeyen, gram };
}
```

Dosyalar çalışıyor ve kısa. Sorun, kuralın söylediği cümlededir. "Teslim edilmemiş bir gönderi
son hareketinden bu yana servis penceresini aştıysa gecikmelidir" cümlesi alan uzmanının
cümlesidir; dosyada duran cümle ise "hareket kodu H30 veya H40 değilse ve durum etiketi
tamamlandı ya da iade değilse" biçimindedir. İkisi aynı şeyi söylüyor olabilir; ama ikincisi
sağlayıcı sözleşmesi değiştiğinde yanlış olur ve yanlış olduğu yer iş kuralı dosyasıdır.

## Korumalı Düzen

İkinci düzende her sağlayıcı için bir çeviri dosyası var. Çeviri dört işi birden yapıyor:
durum kümesini eşliyor, zamanı tek biçime getiriyor, ağırlığı grama çeviriyor ve bulunamayan
kaydı iç modelin `bilinmiyor` durumuna indiriyor.

```js
// korumali/tasiyici-a-ceviri.mjs — A saglayicisinin modelini ic modele cevirir
import { sevkDurumu } from "../saglayici/tasiyici-a.mjs";

const DURUM = { H10: "kabul", H20: "aktarmada", H25: "dagitimda", H30: "teslim-edildi", H40: "iade-edildi" };

export function gonderiGetir(referans) {
  const y = sevkDurumu(referans);
  if (y.hataKodu !== 0) return { gonderiNo: referans, sonDurum: "bilinmiyor", sonHareketAni: 0, agirlikGram: 0 };
  return {
    gonderiNo: referans,
    sonDurum: DURUM[y.hareketKodu] ?? "bilinmiyor",
    sonHareketAni: y.sonHareketEpok,
    agirlikGram: Math.round(y.agirlikLibre * 453.59237),
  };
}
```

```js
// korumali/tasiyici-b-ceviri.mjs — B saglayicisinin modelini ayni ic modele cevirir
import { takip } from "../saglayici/tasiyici-b.mjs";

const DURUM = { alindi: "kabul", yolda: "aktarmada", "dagitima-verildi": "dagitimda",
                tamamlandi: "teslim-edildi", iade: "iade-edildi" };

export function gonderiGetir(referans) {
  const y = takip(referans);
  const sonDurum = DURUM[y.durumEtiketi] ?? "bilinmiyor";
  return {
    gonderiNo: referans,
    sonDurum,
    sonHareketAni: sonDurum === "bilinmiyor" ? 0 : Math.floor(Date.parse(y.sonHareketZamani) / 1000),
    agirlikGram: Math.round(y.agirlikOns * 28.349523125),
  };
}
```

```js
// korumali/gecikme.mjs — is kurali yalniz ic modelin durum kumesini taniyor
const KAPANMIS = ["teslim-edildi", "iade-edildi"];

export function gecikmeliMi(gonderi, pencereSaat, simdiEpok) {
  if (gonderi.sonDurum === "bilinmiyor") return null;
  if (KAPANMIS.includes(gonderi.sonDurum)) return false;
  return (simdiEpok - gonderi.sonHareketAni) / 3600 > pencereSaat;
}
```

```js
// korumali/rapor.mjs — rapor da yalniz ic modeli okur
import { gecikmeliMi } from "./gecikme.mjs";

export function ozet(gonderiler, pencereSaat, simdiEpok) {
  let gecikmeli = 0, bilinmeyen = 0, gram = 0;
  for (const g of gonderiler) {
    const s = gecikmeliMi(g, pencereSaat, simdiEpok);
    if (s === null) bilinmeyen += 1; else if (s) gecikmeli += 1;
    gram += g.agirlikGram;
  }
  return { gecikmeli, bilinmeyen, gram };
}
```

Kural dosyası artık hangi sağlayıcının konuştuğunu bilmiyor. Ağırlık alanının adı da
değişti: iç modelin zaman alanı `sonHareketAni`, A sağlayıcısının `sonHareketEpok` alanı
değil. Aynı sayıyı taşıyan iki adın ayrı tutulması gereksiz görünür; ama adın dışarıdan
alınması, o adın dışarıyla birlikte değişmesi demektir.

## Sızmanın Sayılması

Ölçü üç sayıdan oluşuyor: dış adların ve dış durum değerlerinin geçtiği dosya sayısı, bunların
kaçının çekirdek dosya olduğu ve iş kuralının tanıdığı dış durum değeri sayısı.

```js
// sizma-say.mjs — dis modelin adlarinin ve durum degerlerinin kac dosyada gorundugunu sayar
import { readFileSync } from "node:fs";

const DIS_ADLAR = ["hareketKodu", "sonHareketEpok", "agirlikLibre", "hataKodu",
                   "durumEtiketi", "sonHareketZamani", "agirlikOns"];
const DIS_DEGERLER = ["H10", "H20", "H25", "H30", "H40", "H99",
                      "alindi", "yolda", "dagitima-verildi", "tamamlandi", "iade", "kayit-yok"];
const IC_DEGERLER = ["kabul", "aktarmada", "dagitimda", "teslim-edildi", "iade-edildi", "bilinmiyor"];

const gecen = (metin, adlar) => adlar.filter((a) => new RegExp(`(?<![\\w-])${a}(?![\\w-])`).test(metin));

for (const [duzen, dosyalar, kural] of [
  ["sizan", ["sizan/gecikme.mjs", "sizan/rapor.mjs"], "sizan/gecikme.mjs"],
  ["korumali", ["korumali/tasiyici-a-ceviri.mjs", "korumali/tasiyici-b-ceviri.mjs",
                "korumali/gecikme.mjs", "korumali/rapor.mjs"], "korumali/gecikme.mjs"],
]) {
  const satir = dosyalar.reduce((t, d) => t + readFileSync(d, "utf8").trimEnd().split("\n").length, 0);
  console.log(`${duzen}/ (${dosyalar.length} dosya, ${satir} satir)`);
  let sizan = 0, cekirdekSizan = 0, cekirdek = 0;
  for (const d of dosyalar) {
    const metin = readFileSync(d, "utf8");
    const ad = gecen(metin, DIS_ADLAR), dd = gecen(metin, DIS_DEGERLER), id = gecen(metin, IC_DEGERLER);
    const sinir = d.includes("-ceviri.mjs");
    if (!sinir) cekirdek += 1;
    if (ad.length > 0 || dd.length > 0) { sizan += 1; if (!sinir) cekirdekSizan += 1; }
    console.log(`  ${d.split("/")[1].padEnd(22)} dis ad ${ad.length}  dis deger ${String(dd.length).padStart(2)}  ic deger ${id.length}`);
  }
  console.log(`  dis ad veya deger gecen dosya = ${sizan} / ${dosyalar.length}, cekirdek dosyada = ${cekirdekSizan} / ${cekirdek}`);
  const k = readFileSync(kural, "utf8");
  console.log(`  is kuralinin tanidigi durum degeri: dis ${gecen(k, DIS_DEGERLER).length}, ic ${gecen(k, IC_DEGERLER).length}`);
}
```

```sh
node sizma-say.mjs
```

```
sizan/ (2 dosya, 32 satir)
  gecikme.mjs            dis ad 5  dis deger  6  ic deger 0
  rapor.mjs              dis ad 2  dis deger  0  ic deger 0
  dis ad veya deger gecen dosya = 2 / 2, cekirdek dosyada = 2 / 2
  is kuralinin tanidigi durum degeri: dis 6, ic 0
korumali/ (4 dosya, 51 satir)
  tasiyici-a-ceviri.mjs  dis ad 4  dis deger  5  ic deger 6
  tasiyici-b-ceviri.mjs  dis ad 3  dis deger  5  ic deger 6
  gecikme.mjs            dis ad 0  dis deger  0  ic deger 3
  rapor.mjs              dis ad 0  dis deger  0  ic deger 0
  dis ad veya deger gecen dosya = 2 / 4, cekirdek dosyada = 0 / 2
  is kuralinin tanidigi durum degeri: dis 0, ic 3
```

Sızan düzende iki dosyanın ikisi de dışın sözlüğünü tanıyor ve ikisi de çekirdek dosya:
2/2. Korumalı düzende dışın sözlüğü hâlâ iki dosyada geçiyor — sayı aynı — ama ikisi de sınır
dosyası; çekirdekte 0/2. Sızmanın toplam miktarı azalmıyor, yeri değişiyor. Yozlaşma önleyici
katmanın iddiası bu kadardır: dışın modeli bir yerde görünmek zorundadır, o yer iş kuralı
olmamalıdır.

İkinci satır çifti daha keskin. Sızan düzende iş kuralı altı dış durum değeri tanıyor ve
hiçbir iç durum değeri tanımıyor; iç modelin durum kümesi kodda hiç yok, iki sağlayıcının
kümelerinin birleşimi olarak dolaylı biçimde var. Korumalı düzende kural sıfır dış değer, üç
iç değer tanıyor. Alan kavramı olarak "durum" ilk kez tek bir kümedir.

## Davranış ve Bedel

Çevirinin doğru olduğu iddia edilmez, sınanır. Altı kayıt iki düzende de aynı kararı ve aynı
özeti vermelidir.

```js
// kosum.mjs — iki duzen ayni gecikme kararini ve ayni ozeti veriyor mu
import { gecikmeliMi as sizanKarar } from "./sizan/gecikme.mjs";
import { ozet as sizanOzet } from "./sizan/rapor.mjs";
import { gonderiGetir as aCeviri } from "./korumali/tasiyici-a-ceviri.mjs";
import { gonderiGetir as bCeviri } from "./korumali/tasiyici-b-ceviri.mjs";
import { gecikmeliMi as korumaliKarar } from "./korumali/gecikme.mjs";
import { ozet as korumaliOzet } from "./korumali/rapor.mjs";

const KAYITLAR = [["A", "AA-1041"], ["A", "AA-1042"], ["A", "AA-9999"],
                  ["B", "BB/2210"], ["B", "BB/2211"], ["B", "BB/9999"]];
const PENCERE = 24, SIMDI = 1700100000;

const icModel = KAYITLAR.map(([t, r]) => (t === "A" ? aCeviri(r) : bCeviri(r)));
let ayrilan = 0;
for (let i = 0; i < KAYITLAR.length; i += 1) {
  const a = sizanKarar(KAYITLAR[i][0], KAYITLAR[i][1], PENCERE, SIMDI);
  const b = korumaliKarar(icModel[i], PENCERE, SIMDI);
  if (a !== b) ayrilan += 1;
  console.log(`${KAYITLAR[i][1].padEnd(9)} ${String(icModel[i].sonDurum).padEnd(13)} sizan ${String(a).padEnd(5)} korumali ${b}`);
}
console.log(`ayrilan karar = ${ayrilan} / ${KAYITLAR.length}`);
console.log(`sizan ozet    = ${JSON.stringify(sizanOzet(KAYITLAR, PENCERE, SIMDI))}`);
console.log(`korumali ozet = ${JSON.stringify(korumaliOzet(icModel, PENCERE, SIMDI))}`);
```

```sh
node kosum.mjs
```

```
AA-1041   dagitimda     sizan true  korumali true
AA-1042   teslim-edildi sizan false korumali false
AA-9999   bilinmiyor    sizan null  korumali null
BB/2210   teslim-edildi sizan false korumali false
BB/2211   aktarmada     sizan false korumali false
BB/9999   bilinmiyor    sizan null  korumali null
ayrilan karar = 0 / 6
sizan ozet    = {"gecikmeli":1,"bilinmeyen":2,"gram":5805}
korumali ozet = {"gecikmeli":1,"bilinmeyen":2,"gram":5805}
```

Altı kayıtta karar aynı, özetin üç alanı da aynı. Bulunamayan iki kayıt ve iki farklı ağırlık
birimi dâhil, çeviri sayısal bir fark üretmedi.

Bedel iki dosya ve on dokuz satırdır: 32 satırlık iki dosya yerine 51 satırlık dört dosya.
İkinci bedel satırla ölçülmez: çeviri yalnız eşlediği kadarını geçirir. A sağlayıcısının
yalnız kendisinde bulunan bir kavramı — örneğin gümrük beklemesi — iç modelin durum kümesinde
karşılığı olmadığı sürece iş kuralına ulaşamaz. Bu bir kayıp gibi görünür, oysa kararın
verildiği yeri belirler: yeni bir alan kavramı gerekip gerekmediğine iç model karar verir,
sağlayıcı karar vermez.

Katmanın gerekçesi de buradan çıkar ve her dış sistem için geçerli değildir. Sözleşmesi
kitaplığın denetiminde olan, modeli alanla aynı ayrımları taşıyan bir yukarı akış için katman
yalnız bir dolaylılıktır. Gerekçe, dışın modelinin farklı ayrımlar yapması ve kitaplığa
danışmadan değişmesidir.

## Özet

- Bağdaştırıcı imzaları uzlaştırır, yozlaşma önleyici katman modelleri uzlaştırır; korunan şey
  iç modelin kendi kavramlarıdır.
- İki sağlayıcı durumu altı kod ile beş etiketle, zamanı epok ile ISO metniyle, ağırlığı libre
  ile ons cinsinden veriyordu; bulunamayan kaydı biri hata koduyla, öteki bir durum etiketiyle
  bildiriyordu.
- Sızan düzende dış sözlük iki dosyanın ikisinde de geçti ve ikisi de çekirdek dosyaydı (2/2);
  korumalı düzende yine iki dosyada geçti ama ikisi de sınır dosyasıydı, çekirdekte 0/2.
- İş kuralı sızan düzende altı dış durum değeri tanıyordu ve hiçbir iç değer tanımıyordu;
  korumalı düzende sıfır dış, üç iç değer tanıdı — durum ilk kez tek bir küme oldu.
- Altı kayıtta karar ve özetin üç alanı iki düzende de aynı çıktı; katmanın bedeli 2 dosya ve
  19 satır, ayrıca çevirinin yalnız eşlediği kavramı geçirmesidir.
- Katmanın gerekçesi dışın modelinin farklı ayrımlar yapması ve kitaplığa danışmadan
  değişmesidir; sözleşmesi kitaplığın denetimindeki bir yukarı akış için katman yalnız bir
  dolaylılıktır.

## Sonraki Adım

Buraya kadarki iki savunma da aynı varsayıma dayanıyordu: karşı taraf yabancıdır, arada bir
çeviri durur. İki bağlamın iki ayrı takıma değil aynı takıma ait olduğu ya da aşağı akış
takımının yukarı akışı değiştirme gücünün hiç bulunmadığı durumlarda bu varsayım tutmaz.
Birinci durumda takımlar bir parçayı ortak tutmayı seçebilir; ikinci durumda aşağı akış
çeviriden vazgeçip yukarı akışın modelini olduğu gibi benimseyebilir. İkisi de ölçülebilir
kararlardır. Sonraki ders bu iki işbirliği kalıbını kurar, ortak tutulan parça büyüdüğünde iki
takımın birlikte düzenlemek zorunda kaldığı dosya sayısını sayar ve çeviriden vazgeçmenin
bedelini aynı ölçekte gösterir.
