---
title: 'Tümleştirme Kalıpları'
source: 'https://academia.sh/tr/kurslar/kurumsal-baglam/tumlestirme-kaliplari'
course: 'Kurumsal Bağlam ve Tümleştirme'
language: tr
updated: '2026-08-17T18:10:34+00:00'
license: 'CC BY-SA 4.0'
---

# Tümleştirme Kalıpları

Aynı bağın dört kalıpla kurulup karşılaştırılması: sorgu başına kaynak sisteme giden tur ve istek, tazelik penceresi, tazeleme başına taşınan kayıt, tüketici koduna sızan fiziksel şema adı sayısı ve kaynak tek sütununu yeniden adlandırdığında kırılan bağ ile koordine edilecek sahip sayısı.

Önceki konu kurumun yeteneklerini çıkardı ve her yeteneğin hangi sistemde durduğunu gösterdi. O
eşleme bir soruyu hiç sormadı: sistemler birbirine nasıl bağlanıyor. Bir yetenek bir sistemde
duruyorsa, aynı veriye ihtiyaç duyan ikinci sistem onu hangi yoldan, hangi sıklıkta ve kimin
sorumluluğunda alıyor. Bu ders tek bir bağı alır, onu dört ayrı kalıpla kurar ve dördünü aynı
ölçülerle karşılaştırır.

Kurum ölçeğinde ölçü birimi bağdır. Tek bir sistemin içinde bir çağrının nereye gittiği o sistemin
sahibinin kararıdır; kurum ölçeğinde bağın iki ucunda iki ayrı sahip, iki ayrı bütçe ve iki ayrı
takvim durur. Bu yüzden burada sayılan şey gecikme kadar, bağın kime ne borç yüklediğidir.

**TM1 — kurum kurgudur ve `node` içinde bir veri yapısı olarak yazılır.** Sistemler, sahipleri ve
aralarındaki bağlar bir dizi olarak durur; ölçülen bağ o dizinin bir satırıdır.

**TM2 — tazeleme dönemi zamanlayıcı yerine `tik` ile modellenir.** Gerekçe: dönemin saat karşılığı
(dakika, saat, gece) kalıbın kendisiyle değil işletim kararıyla belirlenir; ölçülen nicelik "kaç
tazeleme dönemi sonra taze" olduğudur ve bu sayı dönemin uzunluğundan bağımsızdır.

**TM3 — mesajlaşma taşıyıcısı bir ekleme kütüğü olarak modellenir.** Kuyruk mekaniği, teslim
garantisi ve tüketici rekabeti Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda ölçüldü;
burada ölçülen şey taşıyıcının kendisi değil, bağın kaynağa ve sahibine ödettiğidir.

## Kurum ve Ölçülen Bağ

Bölgesel kütüphane ağı kurgu bir kurumdur. Katalog sistemi dışarıdan alınmıştır; ödünç,
ücretlendirme ve bildirim servisleri içeride yazılmıştır; üyelik ayrı bir birimin, kimlik
doğrulama belediyenin elindedir. Bu derste ölçülen tek varlık nüsha durumudur: bir kitap
nüshasının rafta olup olmadığı. Bu veriyi katalog yazar, beş sistem okur.

```js
// kurum.mjs — bolgesel kutuphane agi: kurgu bir kurum modeli; dogrudan kosunca kendini yazar
export const SISTEM = [
  { ad: "katalog", sahip: "katalog birimi", disaridan: 1 },
  { ad: "odunc", sahip: "ic gelistirme", disaridan: 0 },
  { ad: "ucretlendirme", sahip: "ic gelistirme", disaridan: 0 },
  { ad: "bildirim", sahip: "ic gelistirme", disaridan: 0 },
  { ad: "raporlama", sahip: "yonetim birimi", disaridan: 0 },
  { ad: "sube-arayuzu", sahip: "sube birimi", disaridan: 0 },
  { ad: "uyelik", sahip: "uyelik birimi", disaridan: 0 },
  { ad: "kimlik", sahip: "belediye bilgi islem", disaridan: 1 },
];
// nusha durumunu katalogdan okuyan baglar; her bag bir kalipla kurulmustur (model)
export const BAG = [
  { hedef: "odunc", kalip: "ortak-db" },
  { hedef: "ucretlendirme", kalip: "ortak-db" },
  { hedef: "raporlama", kalip: "dosya" },
  { hedef: "sube-arayuzu", kalip: "uzak-cagri" },
  { hedef: "bildirim", kalip: "mesaj" },
];
export const sahip = (a) => SISTEM.find((s) => s.ad === a).sahip;

if (process.argv[1].endsWith("kurum.mjs")) {
  console.log(`${SISTEM.length} sistem, ${new Set(SISTEM.map((s) => s.sahip)).size} ayri sahip, ` +
    `${SISTEM.filter((s) => s.disaridan).length} sistem disaridan alinmis`);
  console.log(`katalogdan cikan bag: ${BAG.length}, dokunulan sahip: ` +
    `${new Set(BAG.map((b) => sahip(b.hedef))).size}`);
  for (const k of ["ortak-db", "dosya", "uzak-cagri", "mesaj"]) {
    console.log(`  ${k.padEnd(11)}${BAG.filter((b) => b.kalip === k).length} bag`);
  }
}
```

```
8 sistem, 6 ayri sahip, 2 sistem disaridan alinmis
katalogdan cikan bag: 5, dokunulan sahip: 3
  ortak-db   2 bag
  dosya      1 bag
  uzak-cagri 1 bag
  mesaj      1 bag
```

Kaynak sistem nüsha tablosunun sahibidir ve tabloya dört yoldan erişim verir: tek kaydı yayın
biçiminde döndüren bir uç, aynı biçimde bir anlık görüntü, fiziksel tablonun kopyasını çıkaran bir
döküm ve her değişiklikte yayın biçiminde bir satır eklenen kütük. Fiziksel sütun adları
`SEMA` içinde durur ve yalnız kaynağın kendi kodunda okunur.

```js
// katalog.mjs — nusha tablosunun sahibi olan kaynak sistem; <port>; uzun omurlu surec
import { createServer } from "node:http";
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
const [port] = process.argv.slice(2);
if (!port) { console.log("kullanim: node katalog.mjs <port>"); process.exit(1); }
const IS = 20;                                   // katalogun bir istegi karsilama suresi (ms)
const SEMA = { durum: "durum", sube: "subeKodu" };   // fiziksel sutun adlari; katalogun malidir
const yaz = (s) => writeFileSync("katalog.db", s.map((r) => JSON.stringify(r)).join("\n") + "\n");
const oku = () => readFileSync("katalog.db", "utf8").split("\n").filter(Boolean).map(JSON.parse);
const yayin = (r) => ({ nushaId: r.nushaId, sube: r[SEMA.sube],
  uygun: r[SEMA.durum] === "rafta" });           // disari verilen bicim; katalogun sozudur
if (!existsSync("katalog.db")) {
  yaz([...Array(200).keys()].map((i) => ({ nushaId: 401 + i, subeKodu: 1 + (i % 4),
    durum: i % 5 === 0 ? "oduncte" : "rafta", basimYili: 1990 + (i % 30) })));
}
let yuk = 0;                                     // kaynak sistemin gordugu istek sayisi

createServer(async (istek, yanit) => {
  const u = new URL(istek.url, "http://y");
  if (u.pathname === "/yuk") { yanit.end(String(yuk)); return; }
  yuk += 1;
  await new Promise((c) => setTimeout(c, IS));
  const id = Number(u.searchParams.get("id"));
  if (u.pathname === "/nusha") {                 // uzak cagri: tek kayit, yayin bicimi
    yanit.end(JSON.stringify(yayin(oku().find((r) => r.nushaId === id))));
  } else if (u.pathname === "/anlik") {          // mesaj tuketicisinin ilk dolumu, yayin bicimi
    yanit.end(JSON.stringify(oku().map(yayin)));
  } else if (u.pathname === "/doku") {           // dosya aktarimi: fiziksel tablonun kopyasi
    const s = oku();
    writeFileSync("katalog.doku", s.map((r) => JSON.stringify(r)).join("\n") + "\n");
    yanit.end(String(s.length));
  } else if (u.pathname === "/degistir") {       // katalogun kendi yazma yolu
    const s = oku(), r = s.find((x) => x.nushaId === id);
    r[SEMA.durum] = u.searchParams.get("durum");
    yaz(s);
    appendFileSync("katalog.olay", JSON.stringify(yayin(r)) + "\n");
    yanit.end("tamam");
  } else if (u.pathname === "/goc") {            // katalog kendi sutununu yeniden adlandirir
    yaz(oku().map((r) => { const { [SEMA.durum]: d, ...k } = r; return { ...k, nushaDurumu: d }; }));
    SEMA.durum = "nushaDurumu";
    yanit.end("tamam");
  } else yanit.end("yok");
}).listen(Number(port));
```

## Aynı Bağın Dört Kurulumu

Tüketici tek bir kaynak dosyadan koşar ve kalıbı komut satırından alır. Dört bölümün her biri iki
işlev tanımlar: sorguyu yanıtlayan `SOR` ve tazeleme dönemini temsil eden `TIK`. Bölümler
işaretli olduğu için hangi kalıbın hangi adı okuduğu koddan sayılabilir.

```js
// odunc.mjs — tuketici; <kalip: ortak-db|dosya|uzak-cagri|mesaj> <port> <katalog-port>
import { createServer } from "node:http";
import { existsSync, readFileSync } from "node:fs";
const [kalip, port, kp] = process.argv.slice(2);
if (!port) { console.log("kullanim: node odunc.mjs <kalip> <port> <katalog-port>"); process.exit(1); }
const K = `http://127.0.0.1:${kp}`;
const satir = (d) => readFileSync(d, "utf8").split("\n").filter(Boolean).map(JSON.parse);
const yerel = new Map();                         // tuketicinin kendi kopyasi
let okunan = 0;
const SOR = {}, TIK = {};                        // TIK: tazeleme donemini modelleyen tetik

// --- ortak-db --- tuketici kaynagin fiziksel tablosunu dogrudan okur
SOR["ortak-db"] = (id) => satir("katalog.db").find((r) => r.nushaId === id).durum === "rafta";
TIK["ortak-db"] = () => 0;

// --- dosya --- kaynak fiziksel tabloyu doker, tuketici doku dosyasini bastan okur
SOR["dosya"] = (id) => yerel.get(id);
TIK["dosya"] = async () => {
  await fetch(`${K}/doku`);
  const s = satir("katalog.doku");
  for (const r of s) yerel.set(r.nushaId, r.durum === "rafta");
  return s.length;
};

// --- uzak-cagri --- her sorgu kaynagin yayin bicimini cagirir
SOR["uzak-cagri"] = async (id) => (await (await fetch(`${K}/nusha?id=${id}`)).json()).uygun;
TIK["uzak-cagri"] = () => 0;

// --- mesaj --- ilk dolum yayin bicimindedir, sonra yalniz degisiklik satirlari okunur
SOR["mesaj"] = (id) => yerel.get(id);
TIK["mesaj"] = () => {
  if (!existsSync("katalog.olay")) return 0;
  const s = satir("katalog.olay");
  let n = 0;
  while (okunan < s.length) { yerel.set(s[okunan].nushaId, s[okunan].uygun); okunan += 1; n += 1; }
  return n;
};
if (kalip === "mesaj") for (const r of await (await fetch(`${K}/anlik`)).json()) yerel.set(r.nushaId, r.uygun);

// --- ortak --- kalipten bagimsiz servis yuzeyi
createServer(async (istek, yanit) => {
  const u = new URL(istek.url, "http://y");
  if (u.pathname === "/tik") { yanit.end(String(await TIK[kalip]())); return; }
  yanit.end(String(await SOR[kalip](Number(u.searchParams.get("id")))));
}).listen(Number(port));
```

## Ölçüm

Araç üç aşamada koşar. Önce her tüketiciye yüz sorgu gönderip sorgu başına turu ve bu sırada
kaynak sisteme ulaşan istek sayısını sayar. Sonra kaynakta tek bir nüshanın durumunu değiştirir ve
tüketiciye iki kez sorar: tikten önce ve tikten sonra. Son aşamada kaynak kendi sütununu yeniden
adlandırır ve dört tüketicinin de aynı soruya doğru yanıt verip vermediğine bakılır. Tüketici
kodundaki ad sayımı çalışan süreçlerden değil, kaynak metinden okunur.

```js
// olc.mjs — ayni bagi dort kalipla olcer; <katalog-port> <dort tuketici portu>
import { readFileSync } from "node:fs";
import { BAG, sahip } from "./kurum.mjs";
const arg = process.argv.slice(2);
if (arg.length < 5) { console.log("kullanim: node olc.mjs <katalog-port> <p1> <p2> <p3> <p4>"); process.exit(1); }
const [kp, ...port] = arg;
const KALIP = ["ortak-db", "dosya", "uzak-cagri", "mesaj"];
const UC = Object.fromEntries(KALIP.map((k, i) => [k, `http://127.0.0.1:${port[i]}`]));
const K = `http://127.0.0.1:${kp}`, IS = 20, SORGU = 100;
const metin = (u) => fetch(u).then((y) => y.text());
const sor = (k, id) => metin(`${UC[k]}/sor?id=${id}`);
const tik = (k) => metin(`${UC[k]}/tik`).then(Number);
const o = {};
for (const k of KALIP) { o[k] = {}; await tik(k); }          // ilk dolum, olcumden once

for (const k of KALIP) {                                     // gecikme ve kaynagin gordugu yuk
  const y0 = Number(await metin(`${K}/yuk`));
  const t = performance.now();
  for (let i = 0; i < SORGU; i += 1) await sor(k, 401 + (i % 200));
  o[k].tur = Math.round((performance.now() - t) / SORGU / IS);
  o[k].yuk = Number(await metin(`${K}/yuk`)) - y0;
}
await metin(`${K}/degistir?id=402&durum=oduncte`);           // kaynakta tek kayit degisir
for (const k of KALIP) {
  const once = await sor(k, 402);
  o[k].kayit = await tik(k);
  o[k].pencere = once === "false" ? 0 : (await sor(k, 402)) === "false" ? 1 : -1;
}
const bolum = {};                                            // kalip basina tuketici kodu
for (const p of readFileSync("odunc.mjs", "utf8").split(/^\/\/ --- /m).slice(1)) {
  bolum[p.slice(0, p.indexOf(" "))] = p;
}
const FIZIKSEL = ["katalog.db", "katalog.doku", "durum", "rafta"];   // kaynagin ic semasi
const YAYIN = ["/nusha", "/anlik", "katalog.olay", "uygun"];         // kaynagin verdigi soz
const say = (m, l) => l.filter((a) => m.includes(a)).length;
await metin(`${K}/goc`);                                     // kaynak kendi sutununu adlandirir
for (const k of KALIP) { await tik(k); o[k].dogru = (await sor(k, 403)) === "true"; }

console.log(`${"kalip".padEnd(12)}${"tur".padStart(5)}${"kaynak istegi".padStart(15)}` +
  `${"tazelik tik".padStart(13)}${"tik kaydi".padStart(11)}${"fiziksel ad".padStart(13)}` +
  `${"yayin adi".padStart(11)}${"goc sonrasi".padStart(13)}`);
for (const k of KALIP) {
  console.log(`${k.padEnd(12)}${String(o[k].tur).padStart(5)}${String(o[k].yuk).padStart(15)}` +
    `${String(o[k].pencere).padStart(13)}${String(o[k].kayit).padStart(11)}` +
    `${String(say(bolum[k], FIZIKSEL)).padStart(13)}${String(say(bolum[k], YAYIN)).padStart(11)}` +
    `${(o[k].dogru ? "dogru" : "yanlis").padStart(13)}`);
}
const kirik = BAG.filter((b) => !o[b.kalip].dogru);
console.log(`tek sutun adi degisti: ${kirik.length}/${BAG.length} bag kirildi ` +
  `(${kirik.map((b) => b.hedef).join(", ")}); koordine edilecek sahip ` +
  `${new Set(kirik.map((b) => sahip(b.hedef))).size}/${new Set(BAG.map((b) => sahip(b.hedef))).size}`);
```

```bash
rm -f katalog.db katalog.doku katalog.olay
node katalog.mjs 8981 & K=$!
sleep 0.6
node odunc.mjs ortak-db   8982 8981 & A=$!
node odunc.mjs dosya      8983 8981 & B=$!
node odunc.mjs uzak-cagri 8984 8981 & C=$!
node odunc.mjs mesaj      8985 8981 & D=$!
sleep 0.8
node olc.mjs 8981 8982 8983 8984 8985
kill $K $A $B $C $D
```

```
kalip         tur  kaynak istegi  tazelik tik  tik kaydi  fiziksel ad  yayin adi  goc sonrasi
ortak-db        0              0            0          0            3          0       yanlis
dosya           0              0            1        200            3          0       yanlis
uzak-cagri      1            100            0          0            0          2        dogru
mesaj           0              0            1          1            0          3        dogru
tek sutun adi degisti: 3/5 bag kirildi (odunc, ucretlendirme, raporlama); koordine edilecek sahip 2/3
```

## Gecikme, Yük ve Tazelik

İlk üç kolon kalıpların yüzeyde neye benzediğini veriyor. Uzak çağrı her sorguda kaynağa gidiyor:
sorgu başına bir tur ve yüz sorguda yüz istek. Diğer üç kalıpta sorgu turu sıfır, çünkü yanıt
tüketicinin kendi tarafından geliyor; kaynak sistem yüz sorgunun hiçbirini görmüyor.

Tazelik kolonu bu kazancın karşılığını gösteriyor. Ortak veritabanı ile uzak çağrıda kaynakta
yapılan değişiklik tüketiciye aynı anda yansıyor; iki kalıpta da tazelik penceresi sıfır tiktir.
Dosya aktarımı ve mesajlaşmada değişiklik ancak bir tazeleme döneminden sonra görünüyor. Aradaki
fark dördüncü kolonda: dosya aktarımı tek bir nüshanın durumu değiştiği hâlde her tazelemede iki
yüz kaydın tamamını taşıyor, mesajlaşma bir kayıt taşıyor. Aynı tazelik penceresi, iki yüz kat
farklı taşıma hacmiyle satın alınıyor. Kaynak sisteme düşen iş de buradan çıkıyor: dosya aktarımı
sorgu başına sıfır istek üretir ama her dönemde tabloyu bir baştan sona okur.

Buradan tek başına bir sıralama çıkmıyor. Uzak çağrı tazelik penceresini sıfırlar ve kaynağın
yükünü sorgu sayısına bağlar; dosya aktarımı yükü dönem sayısına bağlar ve tazeliği feda eder;
mesajlaşma dönem sayısına bağlı kalıp taşıma hacmini değişiklik sayısına indirir. Ortak
veritabanı hepsinden ucuz görünüyor: sıfır tur, sıfır istek, sıfır pencere. Bedeli sonraki iki
kolonda duruyor.

## Sahiplik Sızıntısı

Beşinci ve altıncı kolon aynı bağı sahiplik açısından ölçüyor. Ortak veritabanı ve dosya aktarımı
kalıplarında tüketici kodu kaynağın fiziksel şemasından üç ad taşıyor: tablonun adı, sütunun adı
ve o sütunun anlamlı olduğu değer. Üçüncü ad en pahalısıdır, çünkü bir ad değil bir kuraldır.
"Rafta" değerinin uygunluk demek olduğu bilgisi katalog biriminin işletme kuralıdır ve o kural
tüketicinin kodunda ikinci bir kopya olarak durur. Katalog yarın üçüncü bir durum değeri eklerse,
kaynak kod hiç değişmediği hâlde tüketicinin yanıtı yanlışlaşır.

Uzak çağrı ve mesajlaşma kalıplarında fiziksel ad sayısı sıfırdır. Tüketici kaynağın tablosunu
değil, kaynağın dışarıya verdiği biçimi okur; uygunluk kararı kaynağın kodunda tek bir yerde
hesaplanır. Altıncı kolondaki adlar bir kayıp değil, bir sözleşmedir: kaynak sistem o adları
değiştirmemeyi taahhüt etmiştir ve karşılığında tablosunda dilediğini yapabilir.

Ortak veritabanının asıl sorunu bu satırlarda görünüyor. Tablo tek bir sistemin adını taşıyor ama
iki sistemin kodu ona bağlı. Şemanın tek sahibi yok; katalog birimi tabloyu tek başına
değiştiremeyeceğini ancak bağı kuran taraf değişikliği fark ettiğinde öğrenir.

## Değişikliğin Yayılması

Son kolon bunu ölçüyor. Kaynak sistem tek bir sütunun adını değiştiriyor; kendi yayın biçimini
korumak dışında hiçbir şey yapmıyor. Uzak çağrı ve mesajlaşma bağları bu değişiklikten hiç
etkilenmiyor, çünkü ikisi de yayın biçiminden okuyor. Ortak veritabanı ve dosya aktarımı bağları
yanlış yanıt vermeye başlıyor; üstelik gürültüsüzce, çünkü eksik alanın karşılığı hata değil,
tanımsız bir değerdir.

Son satır bu sonucu kurumun tamamına taşıyor. Katalogdan çıkan beş bağın üçü kırılıyor ve
kırılanların ardında iki ayrı sahip duruyor. Kaynak sistemin sahibi için bu, tek satırlık bir
göçün önce iki birimle takvim uzlaştırmasını, sonra üç sistemin uyarlanmasını beklemesi demektir.
Kalıp seçimi burada bir gecikme tercihi olmaktan çıkıp bir koordinasyon borcuna dönüşüyor: hangi
kalıp seçilirse seçilsin veri karşıya geçer, ama kaç sahibin izni olmadan değişemeyeceği kalıba
göre değişir.

## Özet

- Aynı bağ dört kalıpla kuruldu: uzak çağrıda sorgu başına 1 tur ve yüz sorguda 100 kaynak isteği,
  diğer üç kalıpta 0 tur ve 0 istek ölçüldü.
- Tazelik penceresi ortak veritabanı ve uzak çağrıda 0 tik, dosya aktarımı ve mesajlaşmada 1 tik;
  aynı pencere dosya aktarımında 200, mesajlaşmada 1 kayıt taşıyarak alınıyor.
- Ortak veritabanı ve dosya aktarımı tüketici koduna kaynağın fiziksel şemasından 3 ad taşıyor;
  bunların biri sütun adı değil, uygunluk kuralının ikinci kopyasıdır.
- Uzak çağrı ve mesajlaşma fiziksel ad taşımıyor, yerine kaynağın taahhüt ettiği 2 ve 3 yayın adına
  bağlanıyor.
- Kaynak tek bir sütun adını değiştirdiğinde 5 bağın 3'ü sessizce yanlış yanıt vermeye başlıyor ve
  3 sahipten 2'siyle koordinasyon gerekiyor.

## Sonraki Adım

Ölçüm tek bir kaynak ve tek bir tüketiciyle yapıldı; kurum modelinde ise katalogdan çıkan beş bağ
var ve katalog kurumun tek sistemi değil. Her sistem her sisteme kendi seçtiği kalıpla bağlandığında
bağ sayısının sistem sayısıyla nasıl arttığı, yeni bir sistem eklemenin kaç tarafa iş çıkardığı ve
bu bağların ortak bir noktadan geçirilmesinin neyi ucuzlatıp neyi tek bir yerde topladığı hiç
sorulmadı. Sonraki ders aynı kurum modelinde ikili bağları ve merkezî noktayı yan yana kurar; bağ
sayısını, yeni sistemin maliyetini, merkezin düştüğü anda kopan bağ sayısını ve merkezde biriken
iş mantığının kime ait olduğunu sayar.
