---
title: 'Sözleşme Evrimi'
source: 'https://academia.sh/tr/kurslar/servis-mimarileri/sozlesme-evrimi'
course: 'Servis Mimarileri'
language: tr
updated: '2026-08-17T18:06:53+00:00'
license: 'CC BY-SA 4.0'
---

# Sözleşme Evrimi

Bir sözleşme alanının eklenmesi, kaldırılması, adının değişmesi ve zorunlu kılınması: her adımın iki yönde kaç tüketiciyi kırdığı, kırılmanın sessiz mi gürültülü mü olduğu, korumalı okumanın hangi yönü kurtardığı ve geçiş sırasının dağıtım adımı, kırılan çağrı ve taşınan fazla alan olarak ölçülmesi.

Önceki ders bir söz verdi: yayıncı her olayda artan bir `surum` alanı yayımlayacak, tüketici
onu okuyacak. Aynı türden sözler kurs boyunca birikti: `ileti_id` kalıcı olacak, `gun` hep
aynı anlamı taşıyacak. Bu alanlar ayrı dağıtım birimlerinin ortak tuttuğu bir sözleşmedir.

Sorun sözleşmenin varlığı değil, değişmesidir. Servisler ayrı birimler olduğu için sözleşme
tek bir anda değişemez: üretici ile tüketiciler farklı zamanlarda yayına çıkar ve arada iki
sürümün birlikte koştuğu bir pencere açılır. M21/K03 bu sözleşmenin **test** tarafını,
M19/K03 `genişlet–daralt` kalıbını kurmuştu; ikisi de burada tekrarlanmıyor. Buranın konusu
evrim **adımlarının kendisi**: hangi değişiklik kimi kırıyor, sessiz mi kırıyor, ve adımlar
hangi sırayla atılırsa pencere zararsız geçiyor.

## Düzenek

**DT9.** Üretici ve tüketiciler süreç içi işlevlerdir; ölçülen şey sözleşmenin biçimidir,
teslim mekanizması değil.

**DT10.** Kırılma ölçütü: tüketicinin ürettiği iş sonucunun, değişiklikten önceki doğru
değerden farklı olması. Değer farkı **sessiz yanlış**, atılan bir özel durum **hata** sayılır.

**DT11.** Pencere, iki dağıtım adımı arasında akan 50 ileti olarak sabitlenmiştir. Gerçek
uzunluk dağıtım hızına bağlıdır; sabit tutulduğu için planlar karşılaştırılabilir.

## Dört Adım, İki Yön

Ödünç servisi `odunc_verildi` iletisini yayımlıyor, ücretlendirme gecikme ücretini
hesaplıyor. Dört değişiklik türünün her biri iki yönde sınanır: eski tüketici yeni iletiyi
okuduğunda ve yeni tüketici eski iletiyi okuduğunda.

```js
// uyumluluk.mjs — dort evrim adimi, iki yon: eski tuketici yeni iletiyi, yeni tuketici eskiyi okur
const temel = { ileti_id: 1, kitap: 101, uye: 4 };

const adimlar = [
  { ad: "alan ekleme   ", dogru: 28,
    eskiIleti: { ...temel, gun: 14 },
    yeniIleti: { ...temel, gun: 14, gunluk_ucret: 2 },
    eskiT: (i) => 2 * i.gun,
    yeniT: (i) => i.gunluk_ucret * i.gun,
    korumaliT: (i) => (i.gunluk_ucret ?? 2) * i.gun },
  { ad: "alan kaldirma ", dogru: 336,
    eskiIleti: { ...temel, gun: 14, saat: 336 },
    yeniIleti: { ...temel, gun: 14 },
    eskiT: (i) => i.saat,
    yeniT: (i) => i.gun * 24,
    korumaliT: (i) => i.saat ?? i.gun * 24 },
  { ad: "ad degistirme ", dogru: 28,
    eskiIleti: { ...temel, gun: 14 },
    yeniIleti: { ...temel, sure_gun: 14 },
    eskiT: (i) => 2 * i.gun,
    yeniT: (i) => 2 * i.sure_gun,
    korumaliT: (i) => 2 * (i.sure_gun ?? i.gun) },
  { ad: "zorunlu yapma ", dogru: 28,
    eskiIleti: { ...temel, gun: 14 },
    yeniIleti: { ...temel, gun: 14, gunluk_ucret: 2 },
    eskiT: (i) => 2 * i.gun,
    yeniT: (i) => { if (i.gunluk_ucret === undefined) throw new Error("gunluk_ucret zorunlu");
                    return i.gunluk_ucret * i.gun; },
    korumaliT: (i) => { if (i.gunluk_ucret === undefined) throw new Error("gunluk_ucret zorunlu");
                        return i.gunluk_ucret * i.gun; } },
];

const dene = (f, ileti, dogru) => {
  try { return f(ileti) === dogru ? "tamam" : "sessiz yanlis"; }
  catch { return "hata"; }
};
const yaz = (s) => s.padEnd(13);

const say = { tamam: 0, sessiz: 0, hata: 0 }, korumali = { tamam: 0, sessiz: 0, hata: 0 };
const ekle = (k, s) => { k[s === "tamam" ? "tamam" : s === "hata" ? "hata" : "sessiz"] += 1; };

console.log("adim            | eski T <- yeni ileti | yeni T <- eski ileti | korumali T <- eski ileti");
for (const a of adimlar) {
  const yon1 = dene(a.eskiT, a.yeniIleti, a.dogru);
  const yon2 = dene(a.yeniT, a.eskiIleti, a.dogru);
  const yon3 = dene(a.korumaliT, a.eskiIleti, a.dogru);
  ekle(say, yon1); ekle(say, yon2); ekle(korumali, yon1); ekle(korumali, yon3);
  console.log(`${a.ad} | ${yaz(yon1)}        | ${yaz(yon2)}        | ${yaz(yon3)}`);
}
console.log(`duz tuketici : tamam=${say.tamam} sessiz yanlis=${say.sessiz} hata=${say.hata} (8 birlesim)`);
console.log(`korumali     : tamam=${korumali.tamam} sessiz yanlis=${korumali.sessiz} hata=${korumali.hata} (8 birlesim)`);
```

```sh
node uyumluluk.mjs
```

```
adim            | eski T <- yeni ileti | yeni T <- eski ileti | korumali T <- eski ileti
alan ekleme    | tamam                | sessiz yanlis        | tamam        
alan kaldirma  | sessiz yanlis        | tamam                | tamam        
ad degistirme  | sessiz yanlis        | sessiz yanlis        | tamam        
zorunlu yapma  | tamam                | hata                 | hata         
duz tuketici : tamam=3 sessiz yanlis=4 hata=1 (8 birlesim)
korumali     : tamam=5 sessiz yanlis=2 hata=1 (8 birlesim)
```

Sekiz birleşimin yalnız 3'ü sorunsuz. Asıl bulgu, kalan 5'in dağılımıdır: **4 sessiz yanlış,
1 hata.** Eksik bir alan `undefined` olur, hesaba girer ve sonuç sessizce bozulur; hiçbir
yerde hata kaydı oluşmaz. Sözleşmeyi kıran değişikliklerin çoğu gürültü çıkarmaz.

Tek gürültülü satır "zorunlu yapma"dır ve gürültünün nedeni değişikliğin türü değil,
tüketicinin alanı **açıkça denetlemesi**dir. Sessizlik sözleşmenin değil, doğrulamasızlığın
sonucudur.

Sıra kuralı bu tablodan doğrudan okunur. Alan eklemede kırılan yön "yeni tüketici ← eski
ileti"dir; öyleyse **önce üretici** yayına çıkar. Alan kaldırmada kırılan yön "eski tüketici
← yeni ileti"dir; öyleyse **önce tüketici** yayına çıkar. Ad değiştirme ikisinin toplamıdır ve
her iki yön de kırılır: tek bir sıra onu güvenli yapamaz.

## Tüketici Koruması

Son sütun, yeni tüketicinin eski adı da okuyacak biçimde yazılmasıdır. Sessiz yanlış 4'ten
2'ye, tamam 3'ten 5'e çıkıyor. Kurtarılanların üçü de aynı yöndedir: **yeni tüketici ← eski
ileti.** Kalan 2 sessiz yanlış öteki yöndedir ve tüketici kodu onları kurtaramaz — o iletiyi
okuyan tüketici zaten yayına çıkmış eski koddur, ona dokunulamaz.

Buradan kursun en dar kuralı çıkar: **tüketici koruması yalnız bir yönü kapatır; öteki yönü
kapatmak üreticinin işidir.** Üretici, eski adı bir süre daha göndermeye devam ederek kapatır.

## Geçiş Sırası

Ad değişikliği üç planla koşturulur. Dört tüketicinin ikisi alanı okuyor, ikisi okumuyor.

```js
// gecis.mjs — ayni ad degisikligi (gun -> sure_gun), uc gecis plani; pencereler sayilir
const PENCERE = 50;                       // iki adim arasinda akan ileti sayisi

const ileti = {
  v1:    (n) => ({ ileti_id: n, kitap: 101, uye: 4, gun: 14 }),
  gecis: (n) => ({ ileti_id: n, kitap: 101, uye: 4, gun: 14, sure_gun: 14 }),
  v2:    (n) => ({ ileti_id: n, kitap: 101, uye: 4, sure_gun: 14 }),
};
const tuketici = {                        // ilk ikisi alani hic okumuyor, son ikisi okuyor
  uyelik:   { dogru: 4,   v1: (i) => i.uye,     v2: (i) => i.uye },
  bildirim: { dogru: 101, v1: (i) => i.kitap,   v2: (i) => i.kitap },
  ucret:    { dogru: 28,  v1: (i) => 2 * i.gun, v2: (i) => 2 * i.sure_gun },
  gorunum:  { dogru: 14,  v1: (i) => i.gun,     v2: (i) => i.sure_gun },
};
const planlar = {
  "tek adim, once uretici ": [["uretici", "v2"], ["ucret", "v2"], ["gorunum", "v2"]],
  "tek adim, once tuketici": [["ucret", "v2"], ["gorunum", "v2"], ["uretici", "v2"]],
  "genislet-daralt        ": [["uretici", "gecis"], ["ucret", "v2"], ["gorunum", "v2"],
                              ["uretici", "v2"]],
};

for (const [ad, plan] of Object.entries(planlar)) {
  const durum = { uretici: "v1", uyelik: "v1", bildirim: "v1", ucret: "v1", gorunum: "v1" };
  let kirilan = 0, cagri = 0, fazlaAlan = 0, fazlaBayt = 0;
  for (const [birim, surum] of plan) {
    durum[birim] = surum;                 // bir dagitim adimi
    for (let n = 1; n <= PENCERE; n++) {
      const i = ileti[durum.uretici](n);
      if (durum.uretici === "gecis") {          // iki ad birlikte tasiniyor
        fazlaAlan += Object.keys(i).length - Object.keys(ileti.v2(n)).length;
        fazlaBayt += JSON.stringify(i).length - JSON.stringify(ileti.v2(n)).length;
      }
      for (const [t, k] of Object.entries(tuketici)) {
        cagri += 1;
        let s; try { s = k[durum[t]](i); } catch { s = null; }
        if (s !== k.dogru) kirilan += 1;
      }
    }
  }
  console.log(`${ad} | dagitim adimi=${plan.length} tuketici cagrisi=${cagri}` +
    ` | kirilan cagri=${kirilan} | tasinan fazla alan=${fazlaAlan} fazla bayt=${fazlaBayt}`);
}
const okuyan = Object.values(tuketici).filter((k) => String(k.v1).includes("gun")).length;
console.log(`pencere basina ileti=${PENCERE} | tuketici=${Object.keys(tuketici).length}` +
  ` | alani okuyan tuketici=${okuyan}` +
  ` | gecis surumunde fazla alan=${Object.keys(ileti.gecis(1)).length - Object.keys(ileti.v2(1)).length}`);
```

```sh
node gecis.mjs
```

```
tek adim, once uretici  | dagitim adimi=3 tuketici cagrisi=600 | kirilan cagri=150 | tasinan fazla alan=0 fazla bayt=0
tek adim, once tuketici | dagitim adimi=3 tuketici cagrisi=600 | kirilan cagri=150 | tasinan fazla alan=0 fazla bayt=0
genislet-daralt         | dagitim adimi=4 tuketici cagrisi=800 | kirilan cagri=0 | tasinan fazla alan=150 fazla bayt=1350
pencere basina ileti=50 | tuketici=4 | alani okuyan tuketici=2 | gecis surumunde fazla alan=1
```

İki tek adımlı plan **aynı sayıyı** verdi: 600 tüketici çağrısının 150'si kırıldı. Sıra
kırılmanın yerini değiştirir, miktarını değiştirmez — çünkü ad değiştirme her iki yönde de
kırar ve hangi taraf önce giderse öteki taraf o kadar süre yanlış okur.

Genişlet–daralt planı kırılan çağrıyı **0**'a indirir. Bedeli üç kalemdir: dağıtım adımı 3'ten
4'e çıktı, pencere boyunca 150 ileti fazladan bir alan taşıdı (1350 bayt), ve üretici o süre
boyunca aynı bilgiyi iki adla üretmek zorunda kaldı. Çift kodun nerede duracağı bir seçimdir:
üreticide tutulursa 1 birim iki adı birlikte taşır, tüketicide tutulursa alanı okuyan 2 birim
korumalı okuma satırını taşır. Ölçüm ikinci seçeneğin daha pahalı olduğunu söyler — okuyan
tüketici sayısı arttıkça fark büyür.

Dördüncü adım — daraltma — atlanabilir görünür. Atlandığında sistem çalışmaya devam eder;
sözleşmede ölü bir alan kalır ve onu kimin okuduğunu kimse bilmez.

## Özet

- Sekiz değişiklik–yön birleşiminin 3'ü sorunsuz, 4'ü sessiz yanlış, 1'i hata verdi;
  sözleşmeyi kıran değişikliklerin çoğu gürültü çıkarmaz.
- Tek gürültülü satır, tüketicinin alanı açıkça denetlediği satırdır; sessizlik sözleşmenin
  değil doğrulamasızlığın sonucudur.
- Sıra kuralı ölçümden okunur: alan eklemede önce üretici, alan kaldırmada önce tüketici
  yayına çıkar. Ad değiştirme ikisinin toplamıdır ve tek bir sıra onu güvenli yapamaz.
- Korumalı okuma sessiz yanlışı 4'ten 2'ye indirdi; kurtardığı üç birleşimin üçü de "yeni
  tüketici ← eski ileti" yönündedir. Öteki yön yalnız üreticinin eski adı bir süre daha
  göndermesiyle kapanır.
- Ad değişikliğinde iki tek adımlı plan da 600 çağrının 150'sini kırdı; genişlet–daralt 0
  kırdı. Bedeli 1 ek dağıtım adımı, 150 fazla alan ve 1350 fazla bayttır.
- Daraltma adımı atlandığında hiçbir şey kırılmaz; sözleşmede kalıcı olarak ölü bir alan kalır.

## Kurs Kapanışı

Kurs boyunca aynı kütüphane ödünç sistemi on sekiz kez, on sekiz farklı biçimde kuruldu. Her
ders aynı üç sütunu yazdı.

| Ders | Neyi ucuzlattı | Neyi pahalılaştırdı | Doğan arıza kipi |
|---|---|---|---|
| Tek Parça Uygulama | Yerel çağrı, ortak işlem sınırı, tek dağıtım adımı | Ölçekleme birimi tüm kod tabanı; bir satırlık değişiklik 7 modülü yeniden yayınlar | Bir modüldeki sızıntı ya da sonsuz döngü bütün alanları birlikte durdurur |
| Modüler Tek Parça | Bir alanın iç yapısı değişince dokunulan dosya 3'ten 1'e indi | Genel yüz 7'den 11'e çıktı; her alanlar arası ihtiyaç bir dolaylılık ekliyor | Kural yalnız denetleyici koşarken geçerli; tek satırlık ithal sınırı sessizce geri alıyor |
| Servis Yönelimli Mimari | Birimler birbirini ithal etmiyor; yeni birim eklemek mevcutları değiştirmiyor | Ortak modele bağlı birim 1'den 3'e; bir alan adı değişikliği 3 dosyayı birlikte yayına zorluyor | Sahibi güncellenip tüketicisi güncellenmediğinde hata alınmıyor, sınır denetimi sessizce kalkıyor |
| Mikroservisler | Bir oran değişikliği 5 dağıtım biriminin 1'ini yeniden başlattı | İstek başına 9 ağ atlaması, 7 sıralı tur; başlatma 1 adımdan 6 adıma | Zincirin ortasında kırılan istek yarım durum bırakıyor; arıza kısmi |
| Sunucusuz Yaklaşım | Ayakta tutulan süreç 5'ten 1'e düştü; dağıtım birimi tek dosya | İstek başına 4 yeni süreç; depo çağrısı 2'den 9'a çıktı | Süre sınırı işi ortasından keser: kod hatasız, sonuç eksik, kısmi etki depoda kalır |
| Mimari Geçiş Stratejileri | Geri alma 1 adım, yeniden dağıtım 0; yeni biçim tek yolda sınanıyor | Aynı kural 2 kod tabanında; her değişiklik 2 dosyaya yazılıyor | Yalnız bir kod tabanı güncellenince aynı istek iki farklı sonuç veriyor |
| Sınır Çizme Ölçütleri | İyi çizilen sınırda değişiklik tek dağıtım biriminde kalıyor: tek doğrulama, tek geri alma | Kötü çizilende aynı değişiklik 2 birime dokunuyor; 1 ithal kenarı ağ çağrısına, 2 yazma sınır ihlaline dönüyor | İki yayım arasında pencere: yeni oran bir birimde yürürlükte, öteki eski alanı gönderiyor |
| Eşzamanlı Servis Çağrıları | Dört iş adımı üç turda bitiyor; bir servisin kapanması üç ucu ayakta bırakıyor | İstek başına 3 ağ atlaması, 4 süreç, 4 kat işlenen istek | Kısmi arıza; ardılın durum kodu denetlenmezse eksik iş yapılmış başarılı yanıt |
| Eşzamansız Tümleştirme | Yanıt turu 2'den 1'e, yanıt yolundaki birim 3'ten 1'e indi | Sözleşme 3 dağıtım birimine yayıldı; alan başına 2,17 birim bağlandı | Tutarlılık penceresi ve yanıt yolunda hiç görünmeyen tüketici arızası |
| API Ağ Geçidi | Yinelenen kenar satırı 15'ten 6'ya; dokunulan dosya ve istemci çağrısı 3'ten 1'e | İstek başına atlama 3'ten 4'e; yeni bir dağıtım birimi; uç yolu değişince dokunulan dosya yine 2 | Ağ geçidi bütün dış trafiğin bağımlılık kapanışı; yalın servisler kendini savunmuyor |
| Ön Yüz İçin Backend | İstek 1'e, fazla bayt 0'a indi; istemcinin okuduğu aşağı alan adı 6'dan 0'a | 2 yeni dağıtım birimi, 38 satırın 13'ü birebir aynı; aşağı servise ulaşan istek değişmiyor | Aşağı alan adı değişip tek katman güncellenince istemciler sessizce ayrışıyor |
| Servis Ağı | Uygulama kodu 38 satırdan 13'e; ağ ilgisi diye işaretli 18 satır uygulamada kalmadı | Süreç 2'den 3'e, yerel durak 1'den 2'ye, ücret servisine ulaşan istek 4'ten 5'e | Durum koduna bakan yeniden deneme, yinelemeye kapalı ödeme çağrısını yineledi: tahsilat 2 |
| Servis Keşfi | Yeni kopya eklenince dokunulan yapılandırma 3'e karşı 0; kira dolunca 6/6'ya dönüş | 1 ek süreç ve tek arıza noktası, çağrı başına 1 ek çözümleme isteği | Kayıt defteri gerçeğin gecikmeli kopyasını tutar; keşif penceresinde 6 isteğin 2'si ölü adrese gidiyor |
| Dağıtık İşlem Alternatifleri | Kısmi sonuç ortadan kalktı; uygulama kodunda telafi mantığı yazılmıyor | Tur 1'den 2'ye, mesaj `n`'den `2n`'e; kilit 459,5 ms tutuldu, 40 isteğin 6'sı bloke | Belirsiz durum: oy vermiş ama karar almamış katılımcı kilidi süresiz tutar |
| Saga Kalıbı | Kilit penceresi sıfırlandı; hiçbir kaynak başka servisin yanıtı beklenirken tutulmuyor | Her adıma bir telafi; sıra telafi edilebilirliğe bağlandı; koreografide akış bilgisi 1 dosyadan 4'e | Yarım kalmış akış: telafi edilemeyen adımdan sonraki hata 2 geri alınamayan teslim bıraktı |
| Giden Kutusu Kalıbı | "Yapıldı ama duyurulmadı" kalktı: 22 kayıp 0'a indi ve kayıp sessiz olmaktan çıktı | Her istek iki tabloya yazıyor; yeni bir süreç ve yoklama aralığı; kutu budanmazsa büyüyor | İkinci teslim (24 ileti) ve aktarıcı gerilemesi: sistem hata vermez, yalnız geç kalır |
| Olay Sıralama ve Yinelenme | Bozulmuş akıştan doğru son durum: 4 yanlış kitap 0'a, fazladan bildirim 0'a | Tüketici şemasına bir sütun, ileti başına bir okuma (+1471 sorgu), yayıncıya sürüm yükümlülüğü | Sessiz atma: 585 gerçek olay hiçbir hata üretmeden yutuldu |
| Sözleşme Evrimi | Genişlet–daralt kırılan çağrıyı 150'den 0'a; korumalı okuma sessiz yanlışı 4'ten 2'ye indirdi | 3 yerine 4 dağıtım adımı; pencerede 150 fazla alan ve 1350 fazla bayt taşındı | Daraltma atlanınca hiçbir şey kırılmaz; sözleşmede ölü bir alan kalıcı olarak kalır |

Tablo kursun kuralını taşıyor: **bir mimari ancak bu üç sütunla savunulur.** "Daha
ölçeklenebilir" bir karar değildir; hangi birimin ölçeklendiğini, karşılığında kaç ağ
atlamasının ve kaç dağıtım adımının eklendiğini ve hangi yeni arıza kipinin doğduğunu
söylemeyen bir cümle mimari bir gerekçe sayılmaz. Yukarıdaki on sekiz satırın hiçbirinde
üçüncü sütun boş değildir: her sınır, kaldırdığı sorunun yerine yenisini koydu.

Bir şey de eksik kaldı. Bu tablodaki arıza kiplerinin çoğu **sessizdir**: sessizce geri alınan
sınır, sessizce ayrışan istemciler, sessizce yutulan olay, sessizce yanlış okunan alan.
Sistem artık birden çok süreçte koşuyor ve her sınır yeni bir arıza kipi doğurdu; ama bir
isteğin **nerede yavaşladığını ya da nerede kaybolduğunu görmenin** aracı hiç kurulmadı. Her
ders kendi ölçümünü kendi yazdığı sayaçla yaptı; koşan sistemin kendisi bu sayaçları taşımıyor.
Sonraki kurs, **M16/K07 Gözlemlenebilirlik ve Güvenilirlik**, tam olarak bu eksiği ele alır.
