---
title: İstemci–Sunucu
source: 'https://academia.sh/tr/kurslar/mimari-bicemler/istemci-sunucu'
course: 'Mimari Biçemler'
language: tr
updated: '2026-08-17T18:10:36+00:00'
license: 'CC BY-SA 4.0'
---

# İstemci–Sunucu

Rollerin başlatan ve bekleyen olarak ayrılmasının kodda ölçülmesi: teslimat tarafının ücretlendirme tarafı hakkında bilmek zorunda olduğu ad sayısının dört düzenden bire inmesi, ücretlendirme tarafı kendi iç adlarını değiştirdiğinde hangi istemcinin kırıldığı ve bekleyen tarafın başlatan tarafı tanımamasının bedeli olarak yoklama sayısı.

Yapısal biçemler birimlerin nasıl düzenlendiğini ve bağımlılık okunun hangi yöne baktığını ele
aldı. Kara tahta düzeninde birimler birbirini hiç tanımıyordu: ortak bir bilgi alanına yazıyor,
oradan okuyor, buluşmayı alanın kendisi sağlıyordu. Bu konu düzeni değil **konuşma biçimini**
sorar. İki birim arasındaki bir etkileşimde kim başlatır, kim bekler, karşı taraf hakkında
bilinmesi zorunlu olan ne kadardır.

İlk biçem bu üç soruya en keskin yanıtı verendir: bir taraf hep başlatır, öbürü hep bekler ve
bilme yükümlülüğü tek yöne yığılır. Bilgisayar Ağları müfredatındaki İstemci–Sunucu Modeli dersi
ayrımı ağ düzeyinde kurmuştu; rolü belirleyen ölçüt donanım değil, bağlantıyı kimin
başlattığıdır. Adres, dinleme çağrıları ve bağlantı noktası numaraları oraya aittir, burada
yeniden ele alınmaz. Buradaki soru bir basamak yukarıdadır: aynı rol ayrımı tek bir kod
tabanının içinde iki modül arasında nasıl görünür ve neyi ölçer.

## Ölçülecek Şey

Kitaplığın iki bağlamı bu derste birbirine bakar. Teslimat operasyonu bağlamı bir gönderinin
ücretini bilmek zorundadır, ama hesap ücretlendirme bağlamının içindedir. Etkileşimin iki
düzenlenişi karşılaştırılır ve üç sayı alınır: çağıran tarafın karşı taraf hakkında bilmek
zorunda olduğu ad sayısı, karşı taraf kendi içini yeniden düzenlediğinde kırılan istemci sayısı,
bekleyen tarafın başlatan tarafa kurduğu bağ sayısı.

Bağlandığı **kalite niteliği** sürdürülebilirliktir ve nitelik sorusu şudur: ücretlendirme
tarafı kendi iç adlarını değiştirdiğinde teslimat tarafında kaç dosya düzenlenmek zorunda kalır.

## Ücretlendirme Tarafının İçi

Ücret hesabı üç modüle dağılmış durumda. Bu üç dosya iki düzende de birebir aynıdır; değişen
tek şey teslimat tarafının onlara nasıl ulaştığıdır.

```sh
mkdir -p ucret dogrudan istemci sunucu
```

```js
// ucret/bolge.mjs — adresten ucret bolgesi turetir
const BOLGE = { "34": "yakin", "06": "orta", "35": "orta", "65": "uzak" };
export const bolgeBelirle = (il) => BOLGE[il] ?? "uzak";
```

```js
// ucret/tarife.mjs — agirlik kademesi, bolge katsayisi ve asgari ucret
const KATSAYI = { yakin: 1, orta: 1.4, uzak: 2.1 };
const ASGARI = { yakin: 30, orta: 45, uzak: 70 };
export const kademe = (kg) => (kg <= 1 ? 1 : kg <= 5 ? 2 : kg <= 20 ? 3 : 4);
export const tarifeUygula = (bolge, kg) =>
  Math.max(ASGARI[bolge], 25 * kademe(kg) * KATSAYI[bolge]);
```

```js
// ucret/indirim.mjs — sozlesmeli musteri indirimi ve hacim indirimi
const SOZLESME = { "MUS-1": 0.1, "MUS-2": 0.2 };
export const indirimUygula = (tutar, musteri, adet) =>
  tutar * (1 - (SOZLESME[musteri] ?? 0)) * (adet >= 10 ? 0.95 : 1);
```

## İç Adların Doğrudan Çağrıldığı Düzen

Birinci düzende teslimat masası hesabın adımlarını kendisi sıraya koyar. Üç modülü ithal eder,
dört adı çağırır, ara değerleri kendi elinde tutar.

```js
// dogrudan/operasyon.mjs — teslimat masasi ucretlendirmenin ic adlarini kendisi cagirir
import { bolgeBelirle } from "../ucret/bolge.mjs";
import { kademe, tarifeUygula } from "../ucret/tarife.mjs";
import { indirimUygula } from "../ucret/indirim.mjs";

export function fiyatla(gonderi) {
  const bolge = bolgeBelirle(gonderi.il);
  const ham = tarifeUygula(bolge, gonderi.agirlik);
  const net = indirimUygula(ham, gonderi.musteri, gonderi.adet);
  return { bolge, kademe: kademe(gonderi.agirlik), ham, net: Math.round(net * 100) / 100 };
}
```

Bu düzende rol yoktur. İki taraf da eşit görünür, çünkü çağıran taraf çağrılanın iç
sıralamasını biliyor: hangi adım hangisinden önce gelir, hangi ara değer hangi işleve
girer. Adımların sırası bir tarafta, adımların kendisi öbür tarafta durur.

## Rol Ayrımı

İkinci düzende ücretlendirme tarafı tek bir giriş noktası açar. İç adların hiçbiri dışarı
çıkmaz; dışarıya çıkan şey bir **istek** biçimidir. `islem` alanı ne istendiğini söyler,
kalanı isteğin verisidir.

```js
// sunucu/ucretlendirme.mjs — tek giris noktasi; ic adlar disariya cikmaz
import { bolgeBelirle } from "../ucret/bolge.mjs";
import { kademe, tarifeUygula } from "../ucret/tarife.mjs";
import { indirimUygula } from "../ucret/indirim.mjs";

let islenen = 0;

function fiyat(g) {
  const bolge = bolgeBelirle(g.il);
  const ham = tarifeUygula(bolge, g.agirlik);
  const net = indirimUygula(ham, g.musteri, g.adet);
  return { bolge, kademe: kademe(g.agirlik), ham, net: Math.round(net * 100) / 100 };
}

export function istekIsle(istek) {
  islenen += 1;
  if (istek.islem === "fiyatla") return { durum: "tamam", sonuc: fiyat(istek.gonderi) };
  return { durum: "bilinmeyen-islem", islem: istek.islem };
}

export const islenenIstek = () => islenen;
```

```js
// istemci/operasyon.mjs — yalnizca istek bicimini ve tek bir adi taniyor
import { istekIsle } from "../sunucu/ucretlendirme.mjs";

export function fiyatla(gonderi) {
  const yanit = istekIsle({ islem: "fiyatla", gonderi });
  if (yanit.durum !== "tamam") throw new Error(`istek karsilanmadi: ${yanit.durum}`);
  return yanit.sonuc;
}
```

Adımların sırası taraf değiştirdi. Artık bekleyen tarafta duruyor ve başlatan taraf yalnız ne
istediğini söylüyor. Bilinmeyen bir işlem geldiğinde yanıt bir çökme değil, bir durum
değeridir; bu da rolün bir parçasıdır — bekleyen taraf gelen her isteğe yanıt vermek zorundadır.

## Ölçüm

Ölçüm iki istemci dosyasının ithal satırlarını okuyup bilinen ad sayısını çıkarır, iki düzenin
aynı sonucu ürettiğini doğrular, sonra ücretlendirme tarafının kendi içini yeniden
düzenlediği bir durumu kurar: dört iç ad yeniden adlandırılır ve iki istemci de bu yeni ağaçta
koşturulur.

```js
// ad-say.mjs — iki duzende bilinen ad sayisi, sonuc esitligi ve ic adlar degistiginde kirilan istemci
import { cpSync, readFileSync, writeFileSync } from "node:fs";
import { fiyatla as dogrudanFiyatla } from "./dogrudan/operasyon.mjs";
import { fiyatla as istemciFiyatla } from "./istemci/operasyon.mjs";
import { islenenIstek } from "./sunucu/ucretlendirme.mjs";

const ITHAL = /import\s*\{([^}]*)\}\s*from\s*"(\.[^"]+)"/g;
const bilinenAd = (yol) => {
  const bag = [...readFileSync(yol, "utf8").matchAll(ITHAL)];
  const ad = bag.flatMap((m) => m[1].split(",").map((s) => s.trim().split(" ")[0]));
  return { modul: bag.length, ad };
};

for (const yol of ["dogrudan/operasyon.mjs", "istemci/operasyon.mjs"]) {
  const { modul, ad } = bilinenAd(yol);
  console.log(`${yol.padEnd(24)} bilinen modul=${modul} bilinen ad=${ad.length} [${ad.join(" ")}]`);
}

const GONDERI = [
  { il: "34", agirlik: 0.8, musteri: "MUS-1", adet: 3 },
  { il: "65", agirlik: 12, musteri: "MUS-2", adet: 12 },
  { il: "06", agirlik: 30, musteri: "MUS-9", adet: 1 },
];
const a = GONDERI.map(dogrudanFiyatla);
const b = GONDERI.map(istemciFiyatla);
console.log(`sonuc: ${JSON.stringify(a[1])}`);
console.log(`iki duzen esit mi = ${JSON.stringify(a) === JSON.stringify(b)}, istege donusen cagri = ${islenenIstek()}`);

for (const d of ["ucret", "dogrudan", "istemci", "sunucu"]) cpSync(d, `yeni/${d}`, { recursive: true });
const YENI_AD = { bolgeBelirle: "bolgeBul", tarifeUygula: "tarifeHesapla", indirimUygula: "indirimIsle", kademe: "agirlikKademesi" };
for (const yol of ["yeni/ucret/bolge.mjs", "yeni/ucret/tarife.mjs", "yeni/ucret/indirim.mjs", "yeni/sunucu/ucretlendirme.mjs"]) {
  let metin = readFileSync(yol, "utf8");
  for (const [eski, yeni] of Object.entries(YENI_AD)) metin = metin.replaceAll(eski, yeni);
  writeFileSync(yol, metin);
}

for (const yol of ["./yeni/dogrudan/operasyon.mjs", "./yeni/istemci/operasyon.mjs"]) {
  try {
    const { fiyatla } = await import(yol);
    console.log(`${yol.padEnd(32)} ic adlar degistikten sonra net=${fiyatla(GONDERI[1]).net}`);
  } catch (hata) {
    console.log(`${yol.padEnd(32)} kirildi: ${hata.message.split("\n")[0]}`);
  }
}
```

```sh
node ad-say.mjs
```

```
dogrudan/operasyon.mjs   bilinen modul=3 bilinen ad=4 [bolgeBelirle kademe tarifeUygula indirimUygula]
istemci/operasyon.mjs    bilinen modul=1 bilinen ad=1 [istekIsle]
sonuc: {"bolge":"uzak","kademe":3,"ham":157.5,"net":119.7}
iki duzen esit mi = true, istege donusen cagri = 3
./yeni/dogrudan/operasyon.mjs    kirildi: The requested module '../ucret/bolge.mjs' does not provide an export named 'bolgeBelirle'
./yeni/istemci/operasyon.mjs     ic adlar degistikten sonra net=119.7
```

## Sayıların Okunması

Bilinen modül sayısı 3'ten 1'e, bilinen ad sayısı 4'ten 1'e indi. İkinci sayı bilme
yükümlülüğünün ölçüsüdür: rol ayrımından sonra teslimat tarafının ücretlendirme tarafı hakkında
bildiği tek şey bir işlev adı ile bir istek biçimidir. Üç gönderinin ücreti iki düzende birebir
aynı çıktı, yani ölçüm aynı işi yapan iki düzenlenişi karşılaştırıyor.

Son iki satır sürdürülebilirlik sorusunun yanıtıdır. Dört iç ad değiştiğinde doğrudan çağıran
istemci koşum anında kırıldı ve kırılma iletisi eksik adı adıyla söyledi; rol ayrımlı istemci
aynı sonucu üretmeye devam etti ve dosyasına tek karakter dokunulmadı. Düzenlenen dosya sayısı
ilk düzende 4 iç dosya artı 1 istemci dosyası, ikinci düzende 4 iç dosya artı 0 istemci
dosyasıdır. Fark istemci sayısıyla büyür: on istemcili bir kitaplıkta aynı yeniden adlandırma
ilk düzende on dosyayı daha düzenlemeyi gerektirir, ikinci düzende hiçbirini.

Kazanç ücretsiz değildir. Ücretlendirme tarafı bir dosya (giriş noktası) kazandı ve isteğin
karşılanmaması artık bir durum değeri olarak taşınıyor; çağıran her yanıtta bu durumu
denetlemek zorunda. `bilinmeyen-islem` yanıtı derleme anında değil koşum anında görünür —
doğrudan çağıran düzende olmayan bir ada yapılan başvuru ithal hatası olarak daha erken
yakalanırdı.

## Asimetrinin Bedeli

Rol ayrımının ikinci yüzü şudur: bekleyen taraf başlatan tarafı tanımaz. Bunun ölçüsü
ücretlendirme tarafından teslimat tarafına giden ithal sayısıdır. Sonucu, tarife sürümü
değiştiğinde teslimat masasının bunu nasıl öğrendiği belirler — sunucu haber veremediği için
istemci sormak zorundadır. Aşağıdaki betik on iki dilimlik bir takvimde üç ayrı tarife sürümü
kurar ve istemcinin her dilimde bir kez sorduğu durumu sayar.

```js
// yoklama.mjs — bekleyen taraf baslatan tarafi tanimadigi icin haber ancak sorulunca ogrenilir
import { readFileSync } from "node:fs";

const SUNUCU_TARAFI = ["sunucu/ucretlendirme.mjs", "ucret/bolge.mjs", "ucret/tarife.mjs", "ucret/indirim.mjs"];
const geriBag = SUNUCU_TARAFI
  .flatMap((yol) => [...readFileSync(yol, "utf8").matchAll(/from "(\.[^"]+)"/g)])
  .filter((m) => m[1].includes("istemci")).length;
console.log(`sunucu tarafindan istemciye giden ithal = ${geriBag}`);

const TAKVIM = ["T1", "T1", "T1", "T2", "T2", "T2", "T2", "T2", "T3", "T3", "T3", "T3"];
let sorulan = 0;
const istekIsle = (istek) => {
  sorulan += 1;
  return { durum: "tamam", surum: TAKVIM[istek.dilim] };
};

let bilinen = null, ogrenilen = 0, bosSoru = 0;
for (let dilim = 0; dilim < TAKVIM.length; dilim += 1) {
  const { surum } = istekIsle({ islem: "tarifeSurumu", dilim });
  if (surum === bilinen) bosSoru += 1;
  else { bilinen = surum; ogrenilen += 1; }
}
console.log(`takvimdeki surum = ${new Set(TAKVIM).size}, sorulan = ${sorulan}, ogrenilen = ${ogrenilen}, degisim getirmeyen soru = ${bosSoru}`);
```

```sh
node yoklama.mjs
```

```
sunucu tarafindan istemciye giden ithal = 0
takvimdeki surum = 3, sorulan = 12, ogrenilen = 3, degisim getirmeyen soru = 9
```

Geri bağ sayısı sıfırdır ve bu, rolün tanımı gereğidir. Bedeli son satır veriyor: üç sürüm
değişimini öğrenmek on iki soru gerektirdi, dokuz soru hiçbir haber getirmedi. Oran soru
sıklığıyla oynar — istemci daha seyrek sorarsa boş soru azalır ama değişimi geç öğrenir. Bu
oran bir ödünleşimdir ve iki uçtan biri seçilmek zorundadır, çünkü biçemin içinde üçüncü bir
seçenek yoktur. Takvim burada bir modeldir, ölçüm değil; sayılan şey soru ile öğrenilen haber
arasındaki farktır, herhangi bir süre değil.

## Özet

- İstemci ile sunucuyu ayıran ölçüt kodda da başlatma yönüdür: bir taraf isteği kurar, öbürü
  gelen her isteğe yanıt vermek zorundadır.
- Rol ayrımı adımların sırasını taraf değiştirir; teslimat tarafının bildiği modül sayısı 3'ten
  1'e, ad sayısı 4'ten 1'e indi ve iki düzen aynı ücreti üretti.
- Ücretlendirme tarafı dört iç adı yeniden adlandırdığında doğrudan çağıran istemci koşum anında
  kırıldı, rol ayrımlı istemci dosyasına dokunulmadan çalışmaya devam etti.
- Bedel bir giriş noktası dosyası, her yanıtta denetlenecek bir durum değeri ve hatanın ithal
  anından koşum anına kaymasıdır.
- Bekleyen tarafın başlatan tarafa giden ithal sayısı sıfırdır; bunun sonucu üç sürüm değişimini
  öğrenmek için on iki soru sorulması ve dokuz sorunun boş dönmesidir.

## Sonraki Adım

Bu düzende asimetri kasıtlıdır: bir taraf hep başlatır, öbürü hep bekler ve bekleyen taraf
tektir. Kitaplığın işleyişinde bu tekliğin bir karşılığı var — bütün ücret sorularının tek bir
yere gitmesi, o yerin herkes tarafından bilinmesi anlamına gelir. Aynı işi birden çok aktarma
merkezinin yürüttüğü bir düzende soru tersine döner: her merkez hem soran hem yanıtlayan
olursa, bilme yükümlülüğü tek yöne yığılmak yerine dağılır. Sonraki ders bu düzeni ölçer: bir
birimin tanımak zorunda olduğu komşu sayısı, bir tarife güncellemesinin bütün birimlere
ulaşması için gereken ileti sayısı ve bir birim çekildiğinde iş görmeye devam eden birim sayısı.
