---
title: 'Backend Ne Yapar'
source: 'https://academia.sh/tr/kurslar/sunucu-temelleri/backend-ne-yapar'
course: 'Sunucu Tarafı Temelleri'
language: tr
updated: '2026-08-17T18:06:52+00:00'
license: 'CC BY-SA 4.0'
---

# Backend Ne Yapar

Sunucu tarafının üstlendiği üç sorumluluk: durumun tek yerde tutulması, iş kurallarının uygulanma noktası ve istemcinin erişemediği sistemlerle tümleştirme.

Frontend müfredatındaki Uygulama Mimarisi kursu, tarayıcıda çalışan uygulamanın veriyi bir
istek katmanı (request layer) üzerinden aldığını kurmuştu: uygulama bir adrese istek yollar,
gelen yanıtı çözer, durum kodunu hata sözleşmesine eşler ve ekranı günceller. O kursta
isteğin gittiği taraf sabit bir varsayımdı. İnternet Nasıl Çalışır kursu ise isteğin
biçimini tanımladı — istek satırı, başlık, gövde — ama karşı tarafta çalışan programın ne
yaptığını değil.

Bu ders o boşluğu doldurur ve tek bir soruyu yanıtlar: istemcinin karşısında duran taraf
neyi üstlenir ve bu iş neden istemciye bırakılamaz? Yanıt üç sorumluluğa ayrılır —
**kalıcılık** (persistence), **iş kuralları** (business rules) ve **tümleştirme**
(integration). Üçü de aynı gerekçeye dayanır ve o gerekçe bu derste ölçülerek gösterilir.
Müfredat boyunca tek bir uygulama sürecek: bir kütüphane ödünç servisi. Kitap, üye, ödünç
işlemi ve şube kavramları bütün derslerde aynı anlamı taşıyacak.

## Durum Tek İstemcinin Belleğinde Kalamaz

Ödünç servisinin çekirdek verisi bir liste: hangi üyenin hangi kitabı elinde tuttuğu. Bu
liste istemcide tutulursa ne olur? Aşağıdaki program tek bir istemciyi temsil eder; katalog
sabit, ödünç kaydı ise sürecin kendi belleğindedir.

```js
// istemcide-durum.mjs — odunc kaydini yalnizca kendi belleginde tutan istemci
const KATALOG = new Map([
  ["978-0201896831", { ad: "Programlama Sanati", kopya: 2 }],
  ["978-0262033848", { ad: "Algoritmalara Giris", kopya: 1 }],
]);

const oduncler = []; // bu surece ozgu: baska bir calistirma bunu gormez

const oduncAl = (isbn, uye) => {
  const kitap = KATALOG.get(isbn);
  const elde = oduncler.filter((o) => o.isbn === isbn).length;
  if (elde >= kitap.kopya) return "reddedildi: kopya yok";
  oduncler.push({ isbn, uye });
  return "verildi";
};

const [uye, isbn] = process.argv.slice(2);
console.log(`${uye} -> ${oduncAl(isbn, uye)}`);
console.log(`${uye} gorunumunde toplam odunc: ${oduncler.length}`);
```

İki ayrı üye, tek kopyası olan aynı kitabı ister. Her üye kendi istemcisini çalıştırır:

```bash
node istemcide-durum.mjs U-1001 978-0262033848
node istemcide-durum.mjs U-1002 978-0262033848
```

```
U-1001 -> verildi
U-1001 gorunumunde toplam odunc: 1
U-1002 -> verildi
U-1002 gorunumunde toplam odunc: 1
```

Kod hatalı değildir: kopya denetimi vardır ve doğru yazılmıştır. Kusur, denetimin
denetleyecek veriye sahip olmamasıdır. Her çalıştırma kendi boş listesiyle başlar, kendi
listesini doğru sayar ve doğru sonuca varır. Ama sistemde tek kopya vardır ve iki kişiye
verilmiştir. Bu, eşzamanlılıktan değil **yalıtımdan** doğan bir çakışmadır.

Sunucu tarafının ilk görevi bu yalıtımı kırmaktır: ödünç kaydı bütün istemcilerin
paylaştığı tek bir yerde tutulur. Aşağıdaki sunucu aynı kuralı aynı biçimde uygular, tek
farkla — liste süreçlerin dışında, isteklerin ortaklaştığı yerdedir.

```js
// odunc-sunucusu.mjs — odunc kaydini ve kurallari tek yerde tutan sunucu
import { createServer } from "node:http";

const KATALOG = new Map([
  ["978-0201896831", { ad: "Programlama Sanati", kopya: 2 }],
  ["978-0262033848", { ad: "Algoritmalara Giris", kopya: 1 }],
]);
const UYE_SINIRI = 2;
const oduncler = []; // butun istemcilerin paylastigi tek kayit

const govdeOku = (istek) =>
  new Promise((coz, red) => {
    let veri = "";
    istek.on("data", (parca) => (veri += parca));
    istek.on("end", () => { try { coz(veri ? JSON.parse(veri) : {}); } catch (h) { red(h); } });
  });

const yanitla = (yanit, kod, nesne) => {
  yanit.writeHead(kod, { "content-type": "application/json; charset=utf-8" });
  yanit.end(JSON.stringify(nesne));
};

createServer(async (istek, yanit) => {
  yanit.sendDate = false;
  if (istek.method === "GET" && istek.url === "/odunc") return yanitla(yanit, 200, oduncler);
  if (istek.method !== "POST" || istek.url !== "/odunc") return yanitla(yanit, 404, { hata: "yol_yok" });

  const govde = await govdeOku(istek);          // govde.uygun alani okunmaz
  const kitap = KATALOG.get(govde.isbn);
  if (!kitap) return yanitla(yanit, 404, { hata: "kitap_yok" });

  const eldeki = oduncler.filter((o) => o.isbn === govde.isbn).length;
  if (eldeki >= kitap.kopya) return yanitla(yanit, 409, { hata: "kopya_yok", kalan: 0 });

  const acik = oduncler.filter((o) => o.uye === govde.uye).length;
  if (acik >= UYE_SINIRI) return yanitla(yanit, 409, { hata: "uye_siniri", sinir: UYE_SINIRI, acik });

  oduncler.push({ uye: govde.uye, isbn: govde.isbn });
  yanitla(yanit, 201, { durum: "verildi", kalan: kitap.kopya - eldeki - 1 });
}).listen(8421, "127.0.0.1", () => console.log("dinleniyor 127.0.0.1:8421"));
```

Sunucu uzun ömürlü bir süreçtir; komut satırından çalıştırıldığında durdurulana kadar
dinlemeye devam eder. Bu yüzden örnek, sunucuyu başlatan ve sonunda durduran bir betikle
birlikte verilir. Kullanılan bağlantı noktası 8421 keyfîdir ve boş olmalıdır; doluysa iki
dosyada da değiştirilir.

```bash
#!/usr/bin/env bash
# odunc-sunucusu.mjs'yi baslatir, bes istegi sirayla yollar, kaydi okur, durdurur.
node odunc-sunucusu.mjs & sunucu=$!
sleep 0.5

gonder() {
  printf '%-54s -> ' "$1"
  curl -sS -w ' [%{http_code}]\n' -X POST -H 'content-type: application/json' \
    -d "$1" http://127.0.0.1:8421/odunc
}

gonder '{"uye":"U-1001","isbn":"978-0262033848"}'
gonder '{"uye":"U-1002","isbn":"978-0262033848"}'
gonder '{"uye":"U-1002","isbn":"978-0262033848","uygun":true}'
gonder '{"uye":"U-1001","isbn":"978-0201896831"}'
gonder '{"uye":"U-1001","isbn":"978-0201896831"}'

echo "--- sunucudaki kayit ---"
curl -sS http://127.0.0.1:8421/odunc; echo
kill "$sunucu"
```

```
dinleniyor 127.0.0.1:8421
{"uye":"U-1001","isbn":"978-0262033848"}               -> {"durum":"verildi","kalan":0} [201]
{"uye":"U-1002","isbn":"978-0262033848"}               -> {"hata":"kopya_yok","kalan":0} [409]
{"uye":"U-1002","isbn":"978-0262033848","uygun":true}  -> {"hata":"kopya_yok","kalan":0} [409]
{"uye":"U-1001","isbn":"978-0201896831"}               -> {"durum":"verildi","kalan":1} [201]
{"uye":"U-1001","isbn":"978-0201896831"}               -> {"hata":"uye_siniri","sinir":2,"acik":2} [409]
--- sunucudaki kayit ---
[{"uye":"U-1001","isbn":"978-0262033848"},{"uye":"U-1001","isbn":"978-0201896831"}]
```

İkinci istek reddedilmiştir. Aynı kural, aynı kod, farklı sonuç: değişen tek şey, kuralın
baktığı listenin iki isteğin de yazdığı liste olmasıdır. Sunucudaki kayıt iki satırdır ve
tek kopyalı kitap bir kez verilmiştir.

Buna **kalıcılık** denir. Sözcük burada dar anlamda "diske yazma" değildir; ilerideki
derslerde kayıt bir veritabanına taşınacak. Buradaki anlamı, durumun **tek bir istemcinin
ömrünü ve görüş alanını aşması**dır. Ödünç kaydı, onu yaratan istekten sonra da vardır;
başka bir istemci gelip okuyabilir. Bir sistemin hangi verisinin sunucu tarafında durması
gerektiği sorusu, bu ölçüte indirgenir: bu veriyi tek bir istemcinin görmesi yeterli mi?

## Kuralın Uygulandığı Yer ile Gösterildiği Yer

Yukarıdaki çıktının üçüncü satırı ayrıca okunmalıdır. O istek, gövdesine `"uygun": true`
alanını eklemiştir — istemcinin kendi hesabına göre "bu ödünç uygundur" demesidir. Sunucu
aynı yanıtı vermiştir: `kopya_yok`. Çünkü sunucu o alanı hiç okumaz, kararı elindeki
listeden yeniden hesaplar.

Bu ayrım, iş kuralı sorumluluğunun özüdür. Kütüphanenin "bir üye aynı anda en fazla iki
kitap tutabilir" kuralı iki yerde birden görünebilir:

- **Gösterim yeri.** İstemci, sınıra ulaşmış bir üyeye ödünç düğmesini pasif gösterir,
  gerekçeyi yazar, gereksiz bir ağ turu yapmaz. Bu bir kullanıcı deneyimi kararıdır.
- **Uygulama yeri.** Sunucu, gelen her istekte kuralı yeniden değerlendirir ve
  karşılanmıyorsa isteği reddeder. Bu bir doğruluk kararıdır.

İkisi aynı kuralın iki gerçekleştirimi olduğu için birbirinin yerine geçtiği sanılır.
Geçmez. İstemcideki denetim isteği **azaltır**, sunucudaki denetim isteği **bağlar**.
Sunucuya ulaşan bir isteğin gerçekten hangi istemciden geldiği, o istemcinin hangi kodu
çalıştırdığı ve denetimin çalışıp çalışmadığı sunucunun bilgisi dışındadır — yukarıdaki
`curl` çağrıları bunun somut hâlidir. Kuralın tek bağlayıcı gerçekleştirimi, isteğin
ulaştığı taraftakidir.

Buradan bir tasarım ölçütü çıkar: **bir kural yanlış uygulandığında veri tutarsız kalıyorsa
o kural sunucu tarafındadır.** Sınır aşımı ödünç kaydını bozar, bu yüzden sunucudadır. Bir
tarihin ekranda hangi biçimde yazılacağı ise veriyi bozmaz, bu yüzden istemcide kalabilir.

## İstemcinin Erişemediği Sistemler

Üçüncü sorumluluk, ilk ikisinden farklı bir gerekçeye dayanır. Kütüphanenin ceza kayıtları
başka bir ekibin işlettiği ayrı bir serviste durur ve bu servise erişim bir kimlik bilgisiyle
(credential) korunur.

```js
// ceza-servisi.mjs — ayri bir ekibin islettigi ceza kaydi servisi
import { createServer } from "node:http";

const ANAHTAR = process.env.CEZA_ANAHTARI ?? "";
const CEZALAR = new Map([["U-1001", 0], ["U-1002", 12.5]]);

createServer((istek, yanit) => {
  yanit.sendDate = false;
  yanit.setHeader("content-type", "application/json; charset=utf-8");
  if (istek.headers.authorization !== `Bearer ${ANAHTAR}`) {
    return yanit.writeHead(401).end(JSON.stringify({ hata: "yetkisiz" }));
  }
  const uye = new URL(istek.url, "http://yerel").searchParams.get("uye");
  yanit.writeHead(200).end(JSON.stringify({ uye, ceza: CEZALAR.get(uye) ?? 0 }));
}).listen(8422, "127.0.0.1", () => console.log("ceza servisi 127.0.0.1:8422"));
```

Ödünç uygulaması, üyenin durumunu üretirken kendi ödünç kaydını ceza servisinin yanıtıyla
birleştirir. Kimlik bilgisini taşıyan taraf uygulamadır.

```js
// uygulama.mjs — istemciye bakan uygulama; ceza servisine kimlik bilgisiyle kendisi gider
import { createServer } from "node:http";

const ANAHTAR = process.env.CEZA_ANAHTARI ?? "";
const ODUNCLER = [{ uye: "U-1001", isbn: "978-0262033848" }, { uye: "U-1002", isbn: "978-0201896831" }];

createServer(async (istek, yanit) => {
  yanit.sendDate = false;
  yanit.setHeader("content-type", "application/json; charset=utf-8");
  const uye = new URL(istek.url, "http://yerel").searchParams.get("uye");

  const cevap = await fetch(`http://127.0.0.1:8422/ceza?uye=${uye}`, {
    headers: { authorization: `Bearer ${ANAHTAR}` },
  });
  if (!cevap.ok) return yanit.writeHead(502).end(JSON.stringify({ hata: "ceza_servisi_erisilemedi" }));

  const { ceza } = await cevap.json();
  const acik = ODUNCLER.filter((o) => o.uye === uye).length;
  yanit.writeHead(200).end(JSON.stringify({ uye, acikOdunc: acik, ceza, oduncAlabilir: ceza === 0 }));
}).listen(8423, "127.0.0.1", () => console.log("uygulama 127.0.0.1:8423"));
```

```bash
#!/usr/bin/env bash
# Iki sunucuyu ayni gizli anahtarla baslatir, istemcinin gorebildigi yuzeyi olcer.
export CEZA_ANAHTARI="k-9f3a-ornek"
node ceza-servisi.mjs & ceza=$!
node uygulama.mjs & uyg=$!
sleep 0.5

echo "--- istemci ceza servisine dogrudan gidiyor ---"
curl -sS -w ' [%{http_code}]\n' 'http://127.0.0.1:8422/ceza?uye=U-1002'

echo "--- istemci uygulamaya gidiyor ---"
curl -sS -w ' [%{http_code}]\n' 'http://127.0.0.1:8423/uye?uye=U-1002'
curl -sS -w ' [%{http_code}]\n' 'http://127.0.0.1:8423/uye?uye=U-1001'

yanit=$(curl -sS -D - 'http://127.0.0.1:8423/uye?uye=U-1001')
echo "--- uygulama yanitinda (baslik + govde) anahtarin gectigi satir sayisi ---"
printf '%s' "$yanit" | grep -c 'k-9f3a-ornek'

kill "$ceza" "$uyg"
```

```
ceza servisi 127.0.0.1:8422
uygulama 127.0.0.1:8423
--- istemci ceza servisine dogrudan gidiyor ---
{"hata":"yetkisiz"} [401]
--- istemci uygulamaya gidiyor ---
{"uye":"U-1002","acikOdunc":1,"ceza":12.5,"oduncAlabilir":false} [200]
{"uye":"U-1001","acikOdunc":1,"ceza":0,"oduncAlabilir":true} [200]
--- uygulama yanitinda (baslik + govde) anahtarin gectigi satir sayisi ---
0
```

Üç şey ölçülmüştür. Ceza servisine kimlik bilgisi olmadan gidilemez. Uygulama üzerinden
gidildiğinde iki kaynaktan gelen veri tek yanıtta birleşir. Ve uygulamanın yanıtında —
başlıklar dâhil — anahtarın geçtiği satır sayısı sıfırdır.

Son ölçüm belirleyicidir. İstemciye giden her bayt, o istemciyi çalıştıran kişinin
okuyabildiği bir bayttır; tarayıcıda çalışan kod, indirdiği betiği ve aldığı yanıtı
gizleyemez. Dolayısıyla ceza servisinin anahtarı istemciye verilseydi, anahtar herkese
verilmiş olurdu. **Tümleştirme sorumluluğu sunucudadır, çünkü tümleştirmenin gerektirdiği
kimlik bilgisi istemciye taşınamaz.**

Tümleştirme yalnız gizlilikle de sınırlı değildir. Ceza servisi yavaşladığında ne olacağı,
yanıt vermediğinde hangi durum kodunun üretileceği, birden çok kaynağın nasıl birleştirileceği
sunucu tarafının kararlarıdır. Yukarıdaki uygulama bu kararların en yalınını verir: yukarı
akış servisi başarısızsa `502` üretir ve kendi hata sözleşmesini bozmaz.

## Üç Sorumluluğun Ortak Adı

Kalıcılık, iş kuralları ve tümleştirme birbirinden bağımsız üç iş gibi görünür ama tek bir
kavrama dayanır: **doğruluk kaynağı** (source of truth). Bir sistemde her veri parçası için
"buna hangi bileşen karar verir" sorusunun tek bir yanıtı olmalıdır.

Ödünç kaydında bu yanıt sunucudur, çünkü kayıt paylaşılır. Ödünç kuralında sunucudur, çünkü
kararın bağlayıcı olması gerekir. Ceza bilgisinde sunucudur, çünkü kaynağa erişim orada
korunur. İstemci bu üç durumda da bir **görünüm** üretir: sunucudan aldığı veriyi biçimler,
kuralı önceden gösterir, kullanıcıya bekleme yaşatmadan tepki verir. Görünüm üretmek küçük
bir iş değildir — Frontend müfredatı bunun tamamına ayrılmıştır — ama doğruluk kaynağı olmak
başka bir iştir.

Bu ayrım kurulduğunda bir sonraki soru kendiliğinden gelir: her hesap sunucuya mı taşınmalı?
Hayır; sunucuya taşınan her hesap bir ağ turu, bir yanıt gecikmesi ve bir ölçekleme maliyeti
demektir. Sınırın nereden geçeceği ayrı bir karar konusudur.

## Özet

- Durum tek bir istemcinin belleğinde tutulduğunda her istemci kendi görüşüne göre doğru,
  sistem bütününde yanlış karar verir; çalıştırılan örnek tek kopyalı kitabın iki üyeye
  verilmesiyle bunu gösterir.
- Kalıcılık, verinin tek bir istemcinin ömrünü ve görüş alanını aşması demektir; ölçüt,
  veriyi başka istemcilerin de görmesi gerekip gerekmediğidir.
- Bir iş kuralı istemcide **gösterilir**, sunucuda **uygulanır**; istemcinin gövdeye
  eklediği hazır karar alanı sunucu tarafından okunmaz, karar elde bulunan veriden yeniden
  hesaplanır.
- Yanlış uygulandığında veriyi tutarsız bırakan her kural sunucu tarafındadır; yalnız sunumu
  etkileyen kararlar istemcide kalabilir.
- Tümleştirme sunucudadır, çünkü yukarı akış servislerinin kimlik bilgisi istemciye
  taşınamaz; ölçülen yanıtta anahtarın geçtiği satır sayısı sıfırdır.
- Üç sorumluluk tek kavrama dayanır: her veri parçası için doğruluk kaynağının hangi bileşen
  olduğu tek biçimde belirlenmiş olmalıdır.

## Sonraki Adım

Doğruluk kaynağının sunucu olması, her hesabın sunucuda yapılacağı anlamına gelmez. Bir
kitap listesinin sıralanması, bir gecikme cezasının tutarı, bir arama kutusunun sonuçları —
bunların her biri iki tarafta da hesaplanabilir ve seçim ölçülebilir sonuçlar doğurur:
taşınan bayt, ağ turu sayısı, yanıt süresi ve kararın bağlayıcılığı. Sonraki ders bu sınırı
çizer: aynı hesabı iki tarafta da yapıp taşınan veriyi ve süreyi ölçer, sonra ölçümün
karar veremediği bir kalem gösterir — bağlayıcı olması gereken hesabın hangi tarafta
durması gerektiği, başarımdan değil güvenden gelir.
