---
title: 'Servisler Arası Sözleşme'
source: 'https://academia.sh/tr/kurslar/uygulama-katmani/servisler-arasi-sozlesme'
course: 'Uygulama Katmanı ve Servis Etkileşimi'
language: tr
updated: '2026-08-17T18:10:55+00:00'
license: 'CC BY-SA 4.0'
---

# Servisler Arası Sözleşme

Şema değişikliğinin maliyetinin kırılan tüketici sayısıyla ölçülmesi: beş iç tüketicinin okuma kümeleriyle tek adımda yayının kaç servisi birlikte yayımlamaya zorladığının sayılması, aynı değişikliğin genişlet–daralt sırasına bölünerek kırılan tüketicinin sıfıra indirilmesi ve geçiş penceresinde iki adın birden taşınmasının kayıt baytına ve iç sınır bant genişliğine ödettiğinin hesaplanması.

Önceki ders çağıranın bilmek zorunda olduğu adları saydı ve sayının altı ile sekiz arasında
değiştiğini gösterdi. Sayının kendisi tek başına bir şey söylemez; anlamı, o adlardan biri
değiştiğinde ortaya çıkar. Ağ geçidi de tek çağıran değildir — aynı gönderi kaydını gün sonu
ücretlendirme işi, ön yüz için kurulan arayüz, rapor akışı ve teslimat planlayıcı da okur ve her
biri farklı bir alan altkümesine bakar.

Kırıcı değişikliğin tanımı, şemanın iki yönü ve sürüm numarasının farktan türetilmesi Web API
Tasarımı kursunda kuruldu; burada yeniden anlatılmaz. O kurs maliyeti **tek bir eski istemcide**
ölçmüştü. Servis sınırında ölçü değişir: tüketiciler bizim servislerimizdir, sayıları bellidir ve
okuma kümeleri okunabilir. Bu dersin ölçtüğü sayı **kaç tüketicinin kırıldığı**, karşılığında
ölçtüğü sayı **kaç birimin birlikte yayımlanmak zorunda kaldığıdır**.

## Şema, Tüketiciler ve Değişiklik

Ele alınan şema önceki dersin gönderi kaydıdır. Değişiklik önerisi dört adı düzeltiyor ve bir alan
ekliyor: bölge alanı hangi bölge olduğunu söylemiyordu, ağırlık gram cinsindendi, rota yalnız
bölge kodları taşıyordu ve taşıyıcı alanının bir kod olduğu adından anlaşılmıyordu.

```js
// sozlesme/sema.mjs — gonderi kaydinin iki semasi, bes ic tuketici ve kirilma kurali
export const V1 = { takipNo: "dizgi", durum: "dizgi", bolge: "dizgi", guncellenme: "dizgi",
  rota: "dizi-dizgi", tasiyici: "dizgi", agirlikGram: "tamsayi", hacimDm3: "tamsayi",
  sozlesmeNo: "dizgi", cikisSubesi: "dizgi", varisSubesi: "dizgi" };

// V2 dort adi degistiriyor ve bir alan ekliyor; bolge->varisBolge, agirlikGram->agirlikKg,
// rota->rotaAdimlari (tip de degisiyor), tasiyici->tasiyiciKodu.
export const V2 = { takipNo: "dizgi", durum: "dizgi", varisBolge: "dizgi", guncellenme: "dizgi",
  rotaAdimlari: "dizi-nesne", tasiyiciKodu: "dizgi", agirlikKg: "sayi", hacimDm3: "tamsayi",
  sozlesmeNo: "dizgi", cikisSubesi: "dizgi", varisSubesi: "dizgi", teslimTahmini: "dizgi" };

export const ESLEME = { bolge: "varisBolge", agirlikGram: "agirlikKg",
  rota: "rotaAdimlari", tasiyici: "tasiyiciKodu" };

export const TUKETICI = {
  "ag-gecidi": ["durum", "bolge", "guncellenme", "rota"],
  "on-yuz-arayuzu": ["durum", "guncellenme", "varisSubesi"],
  "rapor-akisi": ["takipNo", "durum", "bolge", "tasiyici"],
  "toplu-ucretlendirme": ["takipNo", "sozlesmeNo", "agirlikGram", "hacimDm3"],
  "teslimat-planlayici": ["rota", "tasiyici", "cikisSubesi", "varisSubesi"],
};

// Bir tuketici, okudugu ad semada yoksa ya da tipi degistiyse kirilir.
export const kirilan = (sema, okuma) => Object.entries(okuma)
  .filter(([, alanlar]) => alanlar.some((a) => sema[a] === undefined || sema[a] !== V1[a]))
  .map(([ad]) => ad);

export const ORNEK = {
  takipNo: "TR-4821", durum: "dagitimda", bolge: "35", guncellenme: "2026-03-11T08:24:00Z",
  rota: ["34", "41", "35"], tasiyici: "T3", agirlikGram: 2400, hacimDm3: 12,
  sozlesmeNo: "S7", cikisSubesi: "34-021", varisSubesi: "35-114",
  varisBolge: "35", rotaAdimlari: [{ bolge: "34", an: "T08" }, { bolge: "41", an: "T14" },
    { bolge: "35", an: "T20" }], tasiyiciKodu: "T3", agirlikKg: 2.4, teslimTahmini: "2026-03-12",
};
export const kayit = (sema) => Object.fromEntries(Object.keys(sema).map((a) => [a, ORNEK[a]]));
```

**Genişlet–daralt** (expand–contract), aynı değişikliği üç adıma bölen yayın sırasıdır. Birinci
adımda sağlayıcı yeni adları eskilerin **yanına** ekler ve ikisini birden doldurur. İkinci adımda
tüketiciler tek tek yeni ada geçer. Üçüncü adımda sağlayıcı eski adları kaldırır. Aşağıdaki koşum
iki yayın düzenini aynı değişiklik üzerinde karşılaştırır.

```js
// sozlesme/olc.mjs — ayni degisiklik iki yayin duzeninde: tek adimda ve genislet-daralt sirasiyla
import { V1, V2, ESLEME, TUKETICI, kirilan, kayit } from "./sema.mjs";

const GECIS = { ...V1, ...V2 };                       // genislet adimi: eski ve yeni adlar bir arada
const gocmus = Object.fromEntries(Object.entries(TUKETICI)
  .map(([ad, a]) => [ad, a.map((x) => ESLEME[x] ?? x)]));
const tuketici = Object.keys(TUKETICI).length;

console.log(`sema alani V1 ${Object.keys(V1).length}, gecis ${Object.keys(GECIS).length}, ` +
  `V2 ${Object.keys(V2).length}; ic tuketici ${tuketici}`);
const sayim = {};
for (const alanlar of Object.values(TUKETICI)) for (const a of alanlar) sayim[a] = (sayim[a] ?? 0) + 1;
const paylasilan = Object.entries(sayim).sort((x, y) => y[1] - x[1]).slice(0, 4);
console.log(`en cok okunan alanlar: ${paylasilan.map(([a, n]) => `${a}=${n}`).join(", ")}\n`);

const k1 = kirilan(V2, TUKETICI);
console.log(`tek adimda yayin -> kirilan tuketici ${k1.length}/${tuketici}: ${k1.join(", ")}`);
console.log(`  birlikte yayimlanmasi gereken birim = ${k1.length + 1} (saglayici + kirilanlar)\n`);

const ADIM = [
  ["1 genislet (iki ad birden)", GECIS, TUKETICI, 1],
  ["2 tuketiciler yeni ada gecer", GECIS, gocmus, Object.entries(TUKETICI)
    .filter(([ad, a]) => a.some((x) => ESLEME[x])).length],
  ["3 daralt (eski adlar kalkar)", V2, gocmus, 1],
];
console.log(`${"adim".padEnd(30)}${"semadaki alan".padStart(14)}${"kirilan tuketici".padStart(18)}` +
  `${"yayimlanan birim".padStart(18)}${"kayit bayt".padStart(12)}`);
let birim = 0;
for (const [ad, sema, okuma, n] of ADIM) {
  birim += n;
  const kirik = Object.entries(okuma).filter(([, alanlar]) =>
    alanlar.some((a) => sema[a] === undefined)).length;
  console.log(`${ad.padEnd(30)}${String(Object.keys(sema).length).padStart(14)}` +
    `${`${kirik}/${tuketici}`.padStart(18)}${String(n).padStart(18)}` +
    `${String(Buffer.byteLength(JSON.stringify(kayit(sema)))).padStart(12)}`);
}
console.log(`toplam: 3 yayin adimi, ${birim} birim yayini, 0 kirilan tuketici\n`);

// K01 Kabaca Buyukluk Hesabi: tepe okuma 416.67 istek/s ve okuma cikisi 1.60 Mbit/s
const OKUMA_TEPE = 416.67, K01_CIKIS = 1.6;
const mbit = (bayt) => (OKUMA_TEPE * bayt * 8) / 1e6;
console.log(`${"sema".padEnd(10)}${"kayit bayt".padStart(12)}${"ic sinir Mbit/s".padStart(17)}` +
  `${"K01 okuma cikisina oran".padStart(26)}`);
for (const [ad, s] of [["V1", V1], ["gecis", GECIS], ["V2", V2]]) {
  const b = Buffer.byteLength(JSON.stringify(kayit(s)));
  console.log(`${ad.padEnd(10)}${String(b).padStart(12)}${mbit(b).toFixed(3).padStart(17)}` +
    `${(mbit(b) / K01_CIKIS).toFixed(2).padStart(26)}`);
}
const gB = Buffer.byteLength(JSON.stringify(kayit(GECIS)));
const vB = Buffer.byteLength(JSON.stringify(kayit(V1)));
console.log(`gecis penceresinin bedeli = ${gB - vB} bayt/kayit, ` +
  `${(mbit(gB) - mbit(vB)).toFixed(3)} Mbit/s (kayit ${(gB / vB).toFixed(2)} katina cikiyor)`);
```

```
sema alani V1 11, gecis 16, V2 12; ic tuketici 5
en cok okunan alanlar: durum=3, bolge=2, guncellenme=2, rota=2

tek adimda yayin -> kirilan tuketici 4/5: ag-gecidi, rapor-akisi, toplu-ucretlendirme, teslimat-planlayici
  birlikte yayimlanmasi gereken birim = 5 (saglayici + kirilanlar)

adim                           semadaki alan  kirilan tuketici  yayimlanan birim  kayit bayt
1 genislet (iki ad birden)                16               0/5                 1         406
2 tuketiciler yeni ada gecer              16               0/5                 4         406
3 daralt (eski adlar kalkar)              12               0/5                 1         334
toplam: 3 yayin adimi, 6 birim yayini, 0 kirilan tuketici

sema        kayit bayt  ic sinir Mbit/s   K01 okuma cikisina oran
V1                 228            0.760                      0.48
gecis              406            1.353                      0.85
V2                 334            1.113                      0.70
gecis penceresinin bedeli = 178 bayt/kayit, 0.593 Mbit/s (kayit 1.78 katina cikiyor)
```

## Kırılan Tüketici Sayısı

Bu sayılar **hesap** sınıfındadır: okuma kümelerinden ve şema tanımlarından aritmetikle çıkar.

Tek adımda yayında beş tüketicinin dördü kırılıyor. Kırılmayan tek tüketici, adı değişen dört
alandan hiçbirini okumayan ön yüz arayüzü. Bu satırın sonucu bir sürüm numarası değil, bir yayın
kısıtıdır: sağlayıcı ve dört tüketici **aynı anda** yayımlanmak zorundadır. Beş birimin tek bir
yayında birlikte hareket etmesi, servislere bölmenin gerekçesini geri alır — bölünmenin amacı
birimlerin ayrı ayrı yayımlanabilmesiydi.

İkinci satır maliyetin nereden geldiğini gösteriyor: `durum` alanını üç tüketici, `bolge`,
`guncellenme` ve `rota` alanlarını ikişer tüketici okuyor. Bir alanın değiştirilme maliyeti onu
okuyan tüketici sayısıdır. Önceki dersin ayrımı burada sayıya dönüşüyor — paylaşılan bir kayıt
alanı, tek bir çağıran için yazılmış bir yordam adından pahalıdır.

## Sıraya Bölmenin Sonucu

Alttaki tablo aynı değişikliği üç adıma böldüğünde kırılan tüketicinin her adımda 0 kaldığını
gösteriyor. Birinci adımda şema on bir alandan on altıya çıkıyor ve eski adlar yerinde kaldığı için
hiçbir tüketici değişmiyor. İkinci adımda dört tüketici yeni adlara geçiyor ve bunu **ayrı ayrı**
yapabiliyor; sıraları önemsizdir, çünkü iki ad da doludur. Üçüncü adımda eski adlar kalkıyor ve
şema on iki alana iniyor.

İki düzenin karşılaştırması iki sayıyla yapılır. Tek adımda yayın: 1 yayın adımı, 5 birim aynı
anda, 4 kırılan tüketici. Genişlet–daralt: 3 yayın adımı, 6 birim yayını, 0 kırılan tüketici. Bir
birim fazla yayımlanıyor ve karşılığında hiçbir birim ötekini beklemiyor.

Sıranın gerektirdiği bir kural vardır ve tabloda görünmez: **birinci adım yayımlandıktan sonra
tüketicilerin gerçekten göç ettiğini bilmek gerekir.** Yoksa üçüncü adım tek adımda yayının
kendisine dönüşür. Bunu bilmenin yolu Web API Tasarımı kursunda kurulan sözleşme sınamalarıdır;
tüketici beklentileri yazılıysa üçüncü adımın güvenli olup olmadığı yayından önce sınanabilir.

## Hesaba Geri Dönüş

Alttaki tablo geçişin faturasını veriyor. Gönderi kaydı V1'de 228 bayt; geçiş penceresinde iki ad
birden taşındığı için 406 bayta çıkıyor, yani 1,78 katına. V2'ye inildiğinde 334 baytta kalıyor —
V1'in üstünde, çünkü rota adımları artık nesne taşıyor ve bir alan eklendi.

K01'in tepe okuma hızıyla çarpıldığında iç sınırdan geçen bant genişliği 0,760 Mbit/s'den 1,353
Mbit/s'ye çıkıyor: geçiş penceresinin bedeli 0,593 Mbit/s. Karşılaştırma noktası K01'in okuma
çıkışıdır (1,60 Mbit/s); geçiş penceresinde iç sınır bu sayının 0,85 katına yaklaşıyor, oysa V1'de
0,48 katındaydı.

Buradan çıkan sonuç geçiş penceresinin bir **süre kararı** olmasıdır. Pencere açık kaldığı sürece
her kayıt yüzde yetmiş sekiz fazla bayt taşır; kapatmak için bütün tüketicilerin göç etmesi
beklenir. Uzun pencere kırılma riskini sıfırda tutar ve sürekli bir bant genişliği vergisi öder;
kısa pencere vergiyi keser ve göçü tamamlamayan bir tüketici kalırsa kırılmayı geri getirir. İki
uçtan hangisinin seçileceği, üçüncü adımın güvenli olduğunu gösteren sınamaya bağlıdır.

## Özet

- Servis sınırında şema değişikliğinin ölçüsü kırılan tüketici sayısı ve birlikte yayımlanmak
  zorunda kalan birim sayısıdır; kırıcılık tanımı ve sürüm türetimi Web API Tasarımı kursunda
  kuruldu ve burada tekrarlanmaz.
- Dört adı değiştiren bir öneri beş iç tüketicinin dördünü kırıyor ve sağlayıcıyla birlikte beş
  birimin aynı anda yayımlanmasını gerektiriyor.
- Bir alanın değiştirilme maliyeti onu okuyan tüketici sayısıdır: `durum` üç, `bolge`,
  `guncellenme` ve `rota` ikişer tüketici tarafından okunuyor.
- Genişlet–daralt sırası aynı değişikliği 3 yayın adımı ve 6 birim yayınıyla, 0 kırılan tüketiciyle
  yapıyor; ikinci adımda tüketiciler birbirini beklemeden göç ediyor.
- Sıranın güvenliği üçüncü adıma bağlıdır: göçün tamamlandığı sınanmadan eski adlar kaldırılırsa
  düzen tek adımda yayına döner.
- K01'e dönüş: kayıt 228 baytken geçiş penceresinde 406 bayta (1,78 katı) çıkıyor ve iç sınır
  0,760'tan 1,353 Mbit/s'ye yükseliyor — 0,593 Mbit/s'lik bir pencere vergisi, K01'in 1,60 Mbit/s
  okuma çıkışının 0,85 katı.

## Sonraki Adım

Dört ders sonunda çağrı yapılabilir durumda: servisler durumsuz, adresleri bulunuyor, konuşma
biçimi seçilmiş ve sözleşmeleri kırılmadan evrilebiliyor. Bir soru hâlâ açık ve o soru bu konunun
en baştaki ölçüsüne geri döner. Ağ geçidi iki servisi çağırdığında birinden yanıt gelmezse ne
kadar bekleyecek, kaç kez deneyecek ve bu bekleme uçtan uca yanıta ne ekleyecek. K01 takip
yanıtı için 200 milisaniyelik bir eşik koymuştu ve zincirdeki adım sayısı üçüncü derste 2 olarak
ölçülmüştü. Sonraki ders o eşiği bir bütçe olarak ele alır: zaman aşımı ile deneme sayısının
çarpımının en kötü durumda ne verdiğini, bütçenin adımlara nasıl bölüşüldüğünü ve deneme
bütçesi konmadığında iç istek hızının kaça katlandığını ölçer.
