---
title: 'Ara Katman Zinciri'
source: 'https://academia.sh/tr/kurslar/sunucu-temelleri/ara-katman-zinciri'
course: 'Sunucu Tarafı Temelleri'
language: tr
updated: '2026-08-17T18:06:52+00:00'
license: 'CC BY-SA 4.0'
---

# Ara Katman Zinciri

Her istekte tekrarlanan işleri tek yerde toplayan soğan modeli zincir kurulur; aynı halkalar dört ayrı sırayla çalıştırılıp kimlik denetiminin günlüklemeye göre yeri, hata yakalayıcının kapsamı ve kütük satırlarının doğruluğu ölçülerek karşılaştırılır.

Önceki ders bir isteğin yedi aşamasını ölçtü ve bütün işleri tek bir işleyicinin içine
yazdı. Gerçek bir uygulamada aynı işlerin çoğu her uçta tekrarlanır: gövdenin ayrıştırılması,
kimliğin çözülmesi, isteğin kütüğe yazılması, hatanın yakalanması. Bunlar tek bir uca ait
olmadıkları için **kesişen ilgiler** (cross-cutting concerns) adını alır.

Kesişen ilgileri tek yerde toplayan yapı, isteği sırayla geçiren bir zincirdir. Bu ders o
zinciri kütüphane ödünç servisi için kurar ve bir soruya yanıt arar: sıra ne kadar önemlidir?
Yanıt, aynı halkaların dört ayrı sırayla çalıştırılıp sonuçların karşılaştırılmasıyla
verilecektir.

## Soğan Modeli

Bir **ara katman** (middleware), isteği ve yanıtı temsil eden bir bağlam ile zincirin geri
kalanını çağıran bir fonksiyon alır. Halka işini yapar, `sonraki()` çağırır, denetim geri
döndüğünde ikinci bir iş daha yapabilir. Bu yüzden zincir düz bir boru değil, iç içe geçmiş
katmanlardır: istek dıştan içe girer, yanıt içten dışa çıkar.

Modelin üç sonucu vardır ve üçü de bu dersin ölçümlerini belirler:

- **Bir halka `sonraki()` çağırmazsa zincir orada durur.** Kimlik denetimi başarısız
  olduğunda arkasındaki hiçbir halka çalışmaz.
- **Bir halkanın `sonraki()` sonrası kodu, kendisinden içerideki her şey bittikten sonra
  çalışır.** Süre ölçümü ve kütük çıkış satırı buraya yazılır.
- **Bir halka yalnız kendisinden içerideki hataları yakalayabilir.** Dışarıdaki bir halkanın
  fırlattığı hata ona ulaşmaz.

## Zinciri Kurmak

Aşağıdaki program beş halka tanımlar ve bunları dört ayrı sırada dizer. Sıra komut satırından
seçilir; halkaların kendisi hiç değişmez.

```js
// ara-katman.mjs — istegi sirayla gecen halkalardan bir zincir; sira komut satirindan secilir
import { createServer } from "node:http";

let sayac = 0;

const yaz = (b, durum, govde) => {
  const metin = JSON.stringify(govde);
  b.yanit.writeHead(durum, { "content-type": "application/json; charset=utf-8",
    "content-length": Buffer.byteLength(metin) });
  b.yanit.end(metin);
};

// --- halkalar: her biri (baglam, sonraki) alir; sonraki() cagrilmazsa zincir orada durur ---

const kutukle = async (b, sonraki) => {
  console.log(`${b.id} -> ${b.istek.method} ${b.istek.url}`);
  try {
    await sonraki();
  } finally {
    console.log(`${b.id} <- ${b.yanit.headersSent ? b.yanit.statusCode : "yanit yazilmadi"}`);
  }
};

const hataYakala = async (b, sonraki) => {
  try {
    await sonraki();
  } catch (hata) {
    console.log(`${b.id} !! yakalandi: ${hata.kod ?? hata.name}`);
    yaz(b, hata.kod === "govde_bicimsiz" ? 400 : 500,
      { hata: hata.kod ?? "ic_hata", istekKimligi: b.id });
  }
};

const govdeCoz = async (b, sonraki) => {
  const parcalar = [];
  for await (const parca of b.istek) parcalar.push(parca);
  const metin = Buffer.concat(parcalar).toString();
  try {
    b.govde = metin ? JSON.parse(metin) : {};
  } catch {
    const hata = new Error("govde JSON olarak cozulemedi");
    hata.kod = "govde_bicimsiz";
    throw hata;
  }
  await sonraki();
};

const kimlikDenetle = async (b, sonraki) => {
  const uye = (b.istek.headers.authorization ?? "").replace(/^Uye /, "");
  if (!/^U-\d{4}$/.test(uye)) return yaz(b, 401, { hata: "kimlik_yok", istekKimligi: b.id });
  b.uye = uye;
  await sonraki();
};

const yonlendir = async (b) => {
  const yol = new URL(b.istek.url, "http://yerel").pathname;
  if (yol === "/odunc" && b.istek.method === "POST") {
    return yaz(b, 201, { odunc: { isbn: b.govde?.isbn, uye: b.uye }, istekKimligi: b.id });
  }
  yaz(b, 404, { hata: "yol_yok", istekKimligi: b.id });
};

// --- ayni halkalar, dort ayri sira ---

const ZINCIRLER = {
  "yakalayici-disarida": [hataYakala, kutukle, govdeCoz, kimlikDenetle, yonlendir],
  "kimlik-once": [hataYakala, kimlikDenetle, kutukle, govdeCoz, yonlendir],
  "yakalayici-icte": [kutukle, govdeCoz, hataYakala, kimlikDenetle, yonlendir],
  "kutuk-disarida": [kutukle, hataYakala, govdeCoz, kimlikDenetle, yonlendir],
};

const secim = process.argv[2] ?? "yakalayici-disarida";
const halkalar = ZINCIRLER[secim];
if (!halkalar) {
  console.error(`bilinmeyen zincir: ${secim}`);
  process.exit(2);
}

const zinciriCalistir = (b) => {
  const ilerle = (i) =>
    i === halkalar.length ? Promise.resolve() : halkalar[i](b, () => ilerle(i + 1));
  return ilerle(0);
};

createServer((istek, yanit) => {
  yanit.sendDate = false;
  const b = { istek, yanit, id: `i-${++sayac}` };
  zinciriCalistir(b).catch((hata) => {                  // son care: zincir disina cikan hata
    console.log(`${b.id} XX zincir disina cikti: ${hata.kod ?? hata.name}`);
    if (!yanit.headersSent) yaz(b, 500, { hata: "ic_hata" });
  });
}).listen(8434, "127.0.0.1", () => console.log(`zincir=${secim} 127.0.0.1:8434`));
```

Zincir çalıştırıcısı beş satırdır: `ilerle(i)` i. halkayı çağırır ve ona `sonraki` olarak
`ilerle(i + 1)` verir. Listenin sonuna gelindiğinde çözülmüş bir söz döner. Sunucu
geri çağrısındaki `catch`, zincirden dışarı çıkan bir hatanın süreci düşürmesini engelleyen
**son çare** halkasıdır; ölçümde bunun ne zaman devreye girdiği görünecektir.

Ölçüm her sıra için aynı üç isteği yapar: geçerli bir ödünç isteği, kimlik bilgisi
taşımayan bir istek ve gövdesi bozuk bir istek.

```bash
#!/usr/bin/env bash
# Ayni uc istegi dort ayri halka sirasiyla tekrarlar; her turda yanitlari ve sunucu kutugunu basar.
UC=http://127.0.0.1:8434/odunc

istekler() {
  echo "  [1] gecerli    : $(curl -sS -X POST -H 'authorization: Uye U-4711' \
    -H 'content-type: application/json' -d '{"isbn":"978-0262033848"}' "$UC")"
  echo "  [2] kimliksiz  : $(curl -sS -X POST \
    -H 'content-type: application/json' -d '{"isbn":"978-0262033848"}' "$UC")"
  echo "  [3] bozuk govde: $(curl -sS -X POST -H 'authorization: Uye U-4711' \
    -H 'content-type: application/json' -d '{isbn' "$UC")"
}

for zincir in yakalayici-disarida kimlik-once yakalayici-icte kutuk-disarida; do
  echo "=== zincir: $zincir ==="
  node ara-katman.mjs "$zincir" > "kutuk-$zincir.txt" 2>&1 & s=$!
  sleep 0.6
  istekler
  sleep 0.3
  kill "$s"; wait "$s" 2>/dev/null
  echo "  --- sunucu kutugu ---"
  sed 's/^/  /' "kutuk-$zincir.txt"
done

rm -f kutuk-*.txt
```

```
=== zincir: yakalayici-disarida ===
  [1] gecerli    : {"odunc":{"isbn":"978-0262033848","uye":"U-4711"},"istekKimligi":"i-1"}
  [2] kimliksiz  : {"hata":"kimlik_yok","istekKimligi":"i-2"}
  [3] bozuk govde: {"hata":"govde_bicimsiz","istekKimligi":"i-3"}
  --- sunucu kutugu ---
  zincir=yakalayici-disarida 127.0.0.1:8434
  i-1 -> POST /odunc
  i-1 <- 201
  i-2 -> POST /odunc
  i-2 <- 401
  i-3 -> POST /odunc
  i-3 <- yanit yazilmadi
  i-3 !! yakalandi: govde_bicimsiz
=== zincir: kimlik-once ===
  [1] gecerli    : {"odunc":{"isbn":"978-0262033848","uye":"U-4711"},"istekKimligi":"i-1"}
  [2] kimliksiz  : {"hata":"kimlik_yok","istekKimligi":"i-2"}
  [3] bozuk govde: {"hata":"govde_bicimsiz","istekKimligi":"i-3"}
  --- sunucu kutugu ---
  zincir=kimlik-once 127.0.0.1:8434
  i-1 -> POST /odunc
  i-1 <- 201
  i-3 -> POST /odunc
  i-3 <- yanit yazilmadi
  i-3 !! yakalandi: govde_bicimsiz
=== zincir: yakalayici-icte ===
  [1] gecerli    : {"odunc":{"isbn":"978-0262033848","uye":"U-4711"},"istekKimligi":"i-1"}
  [2] kimliksiz  : {"hata":"kimlik_yok","istekKimligi":"i-2"}
  [3] bozuk govde: {"hata":"ic_hata"}
  --- sunucu kutugu ---
  zincir=yakalayici-icte 127.0.0.1:8434
  i-1 -> POST /odunc
  i-1 <- 201
  i-2 -> POST /odunc
  i-2 <- 401
  i-3 -> POST /odunc
  i-3 <- yanit yazilmadi
  i-3 XX zincir disina cikti: govde_bicimsiz
=== zincir: kutuk-disarida ===
  [1] gecerli    : {"odunc":{"isbn":"978-0262033848","uye":"U-4711"},"istekKimligi":"i-1"}
  [2] kimliksiz  : {"hata":"kimlik_yok","istekKimligi":"i-2"}
  [3] bozuk govde: {"hata":"govde_bicimsiz","istekKimligi":"i-3"}
  --- sunucu kutugu ---
  zincir=kutuk-disarida 127.0.0.1:8434
  i-1 -> POST /odunc
  i-1 <- 201
  i-2 -> POST /odunc
  i-2 <- 401
  i-3 -> POST /odunc
  i-3 !! yakalandi: govde_bicimsiz
  i-3 <- 400
```

## Kimlik Denetimi ile Günlüklemenin Sırası

İlk iki turun istemciye dönen yanıtları **birebir aynıdır**. Üç istek, üç aynı gövde. Sıra
değişikliği istemcinin gördüğü hiçbir şeyi değiştirmemiştir.

Sunucu kütüğünde ise bir satır çifti eksiktir. `kimlik-once` turunda `i-2` hiç görünmez.
Kimlik denetimi günlüklemeden önce geldiği için kimliksiz istek zincirin ikinci halkasında
durmuş, kütük halkasına hiç ulaşmamıştır. Uygulama kimliksiz isteği reddetmiş ama bunu
kaydetmemiştir.

Bunun pratik sonucu ağırdır. Kütüphane servisine bir saat boyunca kimliksiz istek yağdıran
bir istemci, bu sırada hiçbir iz bırakmaz. Reddedilen istekler tam da en çok kaydedilmesi
gereken isteklerdir: hız sınırı kararları, kötüye kullanım tespiti ve hata ayıklama bu
kayıtlara dayanır.

Sıranın ters yönde bir gerekçesi de vardır ve göz ardı edilmemelidir. `kimlik-once`
düzeninde kimliksiz istek, gövdesi hiç okunmadan reddedilir; `yakalayici-disarida`
düzeninde gövde önce belleğe alınır, sonra kimlik denetimi başarısız olur. Büyük gövdeli
istekler söz konusu olduğunda erken reddetme gerçek bir tasarruftur.

İki gerekçenin uzlaştığı düzen, **kütüklemenin en dışta, kimlik denetiminin gövde
ayrıştırmasından önce** olduğu düzendir: her istek kaydedilir, kimliksiz istek yine de
gövdesi okunmadan reddedilir. Ölçümdeki `kutuk-disarida` sırası tam olarak budur ve `i-2`
satırları kütükte yerini almıştır.

## Hata Yakalayıcının Kapsamı Yerinden Gelir

Üçüncü turda istemcinin gördüğü yanıt değişir. `yakalayici-icte` düzeninde bozuk gövdeli
istek `{"hata":"ic_hata"}` alır; diğer üç düzende `{"hata":"govde_bicimsiz"}` ile birlikte
istek kimliği de gelir.

Nedeni doğrudan soğan modelidir. `yakalayici-icte` sırasında `hataYakala`, `govdeCoz`
halkasının **içindedir**. Gövde ayrıştırması hata fırlattığında bu hata dışa doğru yayılır ve
yakalayıcıya hiç uğramaz. Kütükteki `XX zincir disina cikti` satırı bunu söyler: hatayı
zincirin kendisi değil, sunucu geri çağrısındaki son çare yakalamıştır.

Son çare yanıtı kaçınılmaz olarak niteliksizdir. O noktada isteğin hangi halkada
kırıldığı, hangi alanın bozuk olduğu ve kullanıcıya ne söylenmesi gerektiği bilinmez; geriye
`500` ve genel bir ileti kalır. Kullanıcı hatası olan bir durum iç hata olarak raporlanmış,
istek kimliği yanıta konulamamış ve destek isteyen üyenin elinde izlenecek bir numara
kalmamıştır.

Kural şudur: **bir hata yakalayıcı yalnız kendisinden içeride kalanı korur.** Zincirin
tamamını koruması isteniyorsa mümkün olduğunca dışta durmalıdır. Son çare bir yedektir, bir
tasarım değil; devreye girdiği her istek, zincirde eksik kalan bir kapsamın işaretidir.

## Kütük Satırının Doğru Durumu Yazması

Dört turun kütükleri karşılaştırıldığında `yakalayici-disarida` düzeninde bir kusur görünür:
üçüncü istek için `i-3 <- yanit yazilmadi` yazılmış, yakalayıcının kaydı ise ancak ondan
sonra gelmiştir. Kütük satırı, yanıtın `400` olduğunu bilmeden yazılmıştır.

Bu da sıradan gelir. `hataYakala` en dışta olduğunda, `kutukle` halkasının `finally` bloğu
hata dışa doğru yayılırken çalışır — yani yakalayıcı yanıtı yazmadan **önce**. Kütük,
kendisinden dışarıda alınan bir kararı göremez.

`kutuk-disarida` düzeninde iki halkanın yeri değişir: kütükleme en dışta, hata yakalayıcı
onun hemen içinde. Şimdi yakalayıcı yanıtı yazar, denetim `kutukle` halkasına döner ve
`finally` bloğu `i-3 <- 400` yazar. Aynı üç istek, aynı beş halka; yalnız iki halkanın yeri
değişmiştir ve kütük artık doğrudur.

Buradan zincir dizmenin genel kuralı çıkar: **bir halka, gözlemlemek veya değiştirmek
istediği her şeyin dışında olmalıdır.** Kütükleme yanıtın son hâlini görmek istiyorsa yanıtı
üreten her halkanın dışında durur; hata yakalayıcı bütün hataları görmek istiyorsa hata
fırlatabilecek her halkanın dışında durur. İkisi birlikte, kütüklemenin yakalayıcıdan da
dışta olması gerektiğini söyler.

## Özet

- Ara katman zinciri soğan modelidir: istek dıştan içe girer, yanıt içten dışa çıkar; bir
  halkanın `sonraki()` sonrası kodu, kendisinden içerideki her şey bittikten sonra çalışır.
- Kimlik denetimi günlüklemeden önce geldiğinde reddedilen istek kütükte hiç görünmez;
  ölçümde `kimlik-once` turunda `i-2` satırları eksiktir, istemciye dönen yanıtlar ise
  dört düzende de aynıdır.
- Erken kimlik denetimi gövdeyi hiç okumadan reddetme kazancı sağlar; iki gerekçe,
  kütükleme en dışta ve kimlik denetimi gövde ayrıştırmasından önce dizildiğinde uzlaşır.
- Bir hata yakalayıcı yalnız kendisinden içerideki hataları görür; `yakalayici-icte`
  düzeninde bozuk gövde `{"hata":"ic_hata"}` ile yanıtlanmış, diğerlerinde istek kimliğiyle
  birlikte `{"hata":"govde_bicimsiz"}` dönmüştür.
- Kütük satırı yalnız kendisinden içeride alınan kararları görebilir; yakalayıcı kütüklemenin
  dışında kaldığında durum `yanit yazilmadi` olarak kaydedilmiş, sıra düzeltildiğinde
  `i-3 <- 400` doğru değerle yazılmıştır.

## Sonraki Adım

Bu bölüm bir isteğin sunucu tarafındaki yolunu bileşenlerine ayırdı: web sunucusu ve vekil
rolleri, çalışma zamanının süreç modeli, isteğin aşamaları ve kesişen ilgileri sıraya koyan
zincir. Ortaya çıkan zincirin halkaları ise hâlâ tek bir dosyada, hepsi yan yana durur.
Sonraki bölüm bu parçaları taşıyabilecek bir iskelet kurar: dosyalar hangi ölçüte göre
bölünür, hangi modül hangisini çağırabilir ve bu sınır elle mi yoksa bir denetimle mi
korunur? İlk ders proje yapısını ele alır ve bağımlılık yönünü sınayan bir denetim yazar.
