---
title: 'Bağlam Haritası'
source: 'https://academia.sh/tr/kurslar/alan-odakli-tasarim/baglam-haritasi'
course: 'Alan Odaklı Tasarım'
language: tr
updated: '2026-08-17T18:10:41+00:00'
license: 'CC BY-SA 4.0'
---

# Bağlam Haritası

Bağlamlar arası ilişki türleri: yukarı akış ile aşağı akış yönünün ayrılması, haritanın gerçek ithallerden çıkarılması, karşılıklı bağımlı bağlam çifti ile bir bağlam değiştiğinde etkilenen bağlam sayısının iki haritada sayılması ve yukarı akışın adlarının kaç çekirdek dosyada göründüğünün ölçülmesi.

Sınır çizildi ve iki bağlam kendi sözlüğüne kavuştu. Ne var ki bağlamlar birbirinden habersiz
çalışamaz: ücretlendirme indirim oranını sözleşme bağlamından alır, teslimat servis seviyesini
yine oradan alır, sözleşme özeti ise ikisinin sonucunu bir arada gösterir. Sınır bir duvar
değil bir geçittir; geçidin kaç tane olduğu, hangi yöne baktığı ve kimin kimin sözlüğüne
uyduğu tasarım kararıdır.

Bu kararların yazıya geçirilmiş hâline **bağlam haritası** (context map) denir. Harita bir
kutu-ok çizimi değildir; her okun bir türü, bir yönü ve bir bedeli vardır. Bu ders haritayı
kodun gerçek ithallerinden çıkarır ve iki haritayı aynı üç ölçüyle karşılaştırır: karşılıklı
bağımlı bağlam çifti sayısı, bir bağlam değiştiğinde etkilenen bağlam sayısı ve yukarı akışın
adlarının göründüğü çekirdek dosya sayısı.

## Yukarı Akış ve Aşağı Akış

İki bağlam arasındaki okun yönü, hangisinin ötekinin değişimine uymak zorunda olduğunu
söyler. Modeli başkasının uyduğu tarafa **yukarı akış** (upstream), uymak zorunda olan tarafa
**aşağı akış** (downstream) denir. Yön alan bilgisinden değil, karar hakkından çıkar:
sözleşme bağlamı indirim oranının ne anlama geldiğine karar veriyorsa yukarı akıştır,
ücretlendirme o karara uyuyorsa aşağı akıştır.

Bu, Tasarım İlkeleri kursundaki bağımlılık yönünden farklı bir sorudur. Orada ok, kararlılığı
yüksek olan tarafa bakardı; burada ok, alan kararını veren tarafa bakar. İkisi çakışabilir ama
zorunlu değildir: bir bağlam teknik olarak kararsız, alan olarak belirleyici olabilir.

## İlişki Türleri

Aynı iki bağlam arasında birden çok ilişki kurulabilir; hangisinin kurulduğu haritada adıyla
yazılır.

| İlişki | Yön | Ne söyler |
|---|---|---|
| **ortaklık** (partnership) | simetrik | İki takım birlikte başarır veya birlikte başarısız olur; sürümler birlikte planlanır |
| **müşteri–tedarikçi** (customer–supplier) | yukarı → aşağı | Aşağı akışın ihtiyaçları yukarı akışın planına girer; yukarı akış söz verir |
| **açık sunucu servisi** (open host service) | yukarı akış yayımlar | Yukarı akış, birden çok tüketici için tanımlı bir erişim yüzeyi sunar |
| **yayımlanmış dil** (published language) | yukarı akış yayımlar | Yüzeyde geçen adlar ve tipler ayrıca belgelenir; iç model değişse de yayımlanan dil durur |
| **ayrı yollar** (separate ways) | yok | İki bağlam bütünleştirilmez; ihtiyaç varsa dışarıda karşılanır |
| **adsız ilişki** | belirsiz | Bağ vardır ama yönü ve türü yazılmamıştır; her taraf ötekinin modelini okur |

Bu listenin dışında iki işbirliği kalıbı ile dış modele karşı bir savunma kalıbı daha vardır;
üçü kendi derslerinde ele alınır. Bu dersin ölçtüğü şey türlerin kendisi değil, adsız ilişkiyi
adlandırmanın sayısal karşılığıdır.

## Üç Bağlam, İki Düzen

Yukarı akış bağlamı sözleşme ve müşteridir. Kendi sözlüğünde beş ad taşıyor.

```sh
mkdir -p sozlesme adsiz adli
```

```js
// sozlesme/sozlesme.mjs — yukari akis baglami: sozlesme ve musteri kayitlari
const SOZLESMELER = [
  { sozlesmeNo: "S-77", musteriNo: "M-4", tarifeIndirimYuzdesi: 12, servisSeviyeKodu: "SS2", pesinOdemeliMi: true },
  { sozlesmeNo: "S-12", musteriNo: "M-9", tarifeIndirimYuzdesi: 20, servisSeviyeKodu: "SS1", pesinOdemeliMi: false },
];

export const sozlesmeBul = (no) => SOZLESMELER.find((s) => s.sozlesmeNo === no) ?? null;
export const sozlesmeNolari = () => SOZLESMELER.map((s) => s.sozlesmeNo);
```

Birinci düzende ilişkiler adsız. İki aşağı akış bağlamı sözleşmenin kendi adlarını doğrudan
okuyor; rapor ise sözleşme bağlamının içinde duruyor ve iki aşağı akış bağlamını geri
çağırıyor.

```js
// adsiz/ucret.mjs — ucretlendirme, sozlesme baglaminin adlarini dogrudan okuyor
import { sozlesmeBul } from "../sozlesme/sozlesme.mjs";

export function netUcret(brutKurus, sozlesmeNo) {
  const s = sozlesmeBul(sozlesmeNo);
  if (s === null) return brutKurus;
  const indirimli = Math.round(brutKurus * (1 - s.tarifeIndirimYuzdesi / 100));
  return s.pesinOdemeliMi ? Math.round(indirimli * 0.98) : indirimli;
}
```

```js
// adsiz/teslimat.mjs — teslimat, sozlesme baglaminin adlarini dogrudan okuyor
import { sozlesmeBul } from "../sozlesme/sozlesme.mjs";

const PENCERE = { SS1: 24, SS2: 48, SS3: 72 };

export function teslimPenceresiSaat(sozlesmeNo) {
  const s = sozlesmeBul(sozlesmeNo);
  return s === null ? 72 : PENCERE[s.servisSeviyeKodu];
}
```

```js
// adsiz/rapor.mjs — sozlesme baglaminda duran rapor, iki asagi akis baglamini geri cagiriyor
import { sozlesmeBul, sozlesmeNolari } from "../sozlesme/sozlesme.mjs";
import { netUcret } from "./ucret.mjs";
import { teslimPenceresiSaat } from "./teslimat.mjs";

export const sozlesmeOzeti = (brutKurus) => sozlesmeNolari().map((no) => ({
  odeyen: sozlesmeBul(no).musteriNo,
  net: netUcret(brutKurus, no),
  pencere: teslimPenceresiSaat(no),
}));
```

İkinci düzende her aşağı akış bağlamı yukarı akışın adlarını kendi diline çeviren bir sınır
dosyası taşıyor; çekirdek dosyalar yalnız kendi dillerini konuşuyor. Rapor da yerini
değiştiriyor: ücretlendirme bağlamının bir raporu olduğu kabul edilip oraya taşınıyor ve
teslimattan yalnız yayımlanmış pencere işlevini kullanıyor.

```js
// adli/ucretlendirme-ceviri.mjs — yukari akis adlarini ucretlendirmenin diline cevirir
import { sozlesmeBul, sozlesmeNolari } from "../sozlesme/sozlesme.mjs";

export const sozlesmeListesi = () => sozlesmeNolari();

export function ucretKosullari(sozlesmeNo) {
  const s = sozlesmeBul(sozlesmeNo);
  if (s === null) return { odeyen: null, indirimOrani: 0, pesinIndirimi: 0 };
  return { odeyen: s.musteriNo, indirimOrani: s.tarifeIndirimYuzdesi / 100,
           pesinIndirimi: s.pesinOdemeliMi ? 0.02 : 0 };
}
```

```js
// adli/ucret.mjs — ucretlendirme cekirdegi: yalniz kendi dilini konusur
import { ucretKosullari } from "./ucretlendirme-ceviri.mjs";

export function netUcret(brutKurus, sozlesmeNo) {
  const { indirimOrani, pesinIndirimi } = ucretKosullari(sozlesmeNo);
  return Math.round(Math.round(brutKurus * (1 - indirimOrani)) * (1 - pesinIndirimi));
}
```

```js
// adli/teslimat-ceviri.mjs — yukari akis adlarini teslimatin diline cevirir
import { sozlesmeBul } from "../sozlesme/sozlesme.mjs";

const PENCERE = { SS1: 24, SS2: 48, SS3: 72 };

export function teslimKosullari(sozlesmeNo) {
  const s = sozlesmeBul(sozlesmeNo);
  return { pencereSaat: s === null ? 72 : PENCERE[s.servisSeviyeKodu] };
}
```

```js
// adli/teslimat.mjs — teslimat cekirdegi: yalniz kendi dilini konusur
import { teslimKosullari } from "./teslimat-ceviri.mjs";

export const teslimPenceresiSaat = (sozlesmeNo) => teslimKosullari(sozlesmeNo).pencereSaat;
```

```js
// adli/rapor.mjs — ucretlendirme baglaminda rapor: kendi dili ve teslimatin yayimladigi pencere
import { sozlesmeListesi, ucretKosullari } from "./ucretlendirme-ceviri.mjs";
import { netUcret } from "./ucret.mjs";
import { teslimPenceresiSaat } from "./teslimat.mjs";

export const sozlesmeOzeti = (brutKurus) => sozlesmeListesi().map((no) => ({
  odeyen: ucretKosullari(no).odeyen,
  net: netUcret(brutKurus, no),
  pencere: teslimPenceresiSaat(no),
}));
```

Çevirinin yaptığı iş adların değiştirilmesinden fazlasıdır. `tarifeIndirimYuzdesi` yüzde
cinsinden bir tam sayıdır, ücretlendirme ise oran ister; `pesinOdemeliMi` bir ödeme koşuludur,
ücretlendirme ondan bir indirim oranı türetir; `musteriNo` sözleşme bağlamının kimliğidir,
ücretlendirme aynı kişiyi `odeyen` diye adlandırır. Sınır dosyası bu üç dönüşümün durduğu
yerdir.

## Haritanın Koddan Çıkarılması

Harita elle yazıldığında koddan sapabilir. Aşağıdaki betik her dosyanın ithallerini okuyup
bağlam düzeyinde yönlü kenarları çıkarıyor, sonra üç ölçüyü hesaplıyor: karşılıklı bağımlı
bağlam çifti, bir bağlam değiştiğinde geçişli olarak etkilenen bağlam sayısı ve yukarı akış
sözlüğünden ad geçen dosyaların kaçının çekirdek dosya olduğu.

```js
// harita-cikar.mjs — gercek ithallerden baglam haritasini cikarir, yayilmayi ve sizmayi olcer
import { readFileSync } from "node:fs";
import { dirname, join, normalize } from "node:path";

const BAGLAM = {
  "sozlesme/sozlesme.mjs": "sozlesme",
  "adsiz/rapor.mjs": "sozlesme", "adsiz/ucret.mjs": "ucretlendirme", "adsiz/teslimat.mjs": "teslimat",
  "adli/ucretlendirme-ceviri.mjs": "ucretlendirme", "adli/ucret.mjs": "ucretlendirme",
  "adli/rapor.mjs": "ucretlendirme",
  "adli/teslimat-ceviri.mjs": "teslimat", "adli/teslimat.mjs": "teslimat",
};
const SOZLUK = ["tarifeIndirimYuzdesi", "servisSeviyeKodu", "pesinOdemeliMi", "musteriNo"];
const SINIR = (d) => d.includes("-ceviri.mjs");

function kenarlar(dosyalar) {
  const kume = new Set();
  for (const d of dosyalar) {
    for (const [, yol] of readFileSync(d, "utf8").matchAll(/from "([^"]+)"/g)) {
      const a = BAGLAM[d], b = BAGLAM[normalize(join(dirname(d), yol))];
      if (a !== b) kume.add(`${a}->${b}`);
    }
  }
  return [...kume].sort();
}

function etkilenen(kenar, hedef) {
  const yigit = [hedef], gorulen = new Set();
  while (yigit.length > 0) {
    const b = yigit.pop();
    for (const k of kenar) {
      const [a, v] = k.split("->");
      if (v === b && a !== hedef && !gorulen.has(a)) { gorulen.add(a); yigit.push(a); }
    }
  }
  return gorulen;
}

for (const [ad, dosyalar] of [
  ["adsiz", ["sozlesme/sozlesme.mjs", "adsiz/ucret.mjs", "adsiz/teslimat.mjs", "adsiz/rapor.mjs"]],
  ["adli", ["sozlesme/sozlesme.mjs", "adli/ucretlendirme-ceviri.mjs", "adli/ucret.mjs",
            "adli/rapor.mjs", "adli/teslimat-ceviri.mjs", "adli/teslimat.mjs"]],
]) {
  const kenar = kenarlar(dosyalar);
  const karsilikli = kenar.filter((k) => kenar.includes(k.split("->").reverse().join("->"))).length / 2;
  console.log(`${ad}: ${dosyalar.length} dosya, ${kenar.length} yonlu kenar, ${karsilikli} karsilikli baglam cifti`);
  for (const k of kenar) console.log(`  ${k}`);
  let toplam = 0;
  for (const b of ["sozlesme", "ucretlendirme", "teslimat"]) {
    const e = etkilenen(kenar, b);
    toplam += e.size;
    const liste = e.size > 0 ? ` (${[...e].sort().join(", ")})` : "";
    console.log(`  ${b.padEnd(14)} degisirse etkilenen baglam = ${e.size}${liste}`);
  }
  console.log(`  toplam yayilma = ${toplam}`);
  const sizan = dosyalar.filter((d) => BAGLAM[d] !== "sozlesme"
    && SOZLUK.some((n) => new RegExp(`\\b${n}\\b`).test(readFileSync(d, "utf8"))));
  const cekirdek = dosyalar.filter((d) => BAGLAM[d] !== "sozlesme" && !SINIR(d));
  console.log(`  yukari akis adi gecen dosya = ${sizan.length} (${sizan.join(", ")})`);
  console.log(`  bunlardan cekirdek dosya = ${sizan.filter((d) => !SINIR(d)).length} / ${cekirdek.length}`);
}
```

```sh
node harita-cikar.mjs
```

```
adsiz: 4 dosya, 4 yonlu kenar, 2 karsilikli baglam cifti
  sozlesme->teslimat
  sozlesme->ucretlendirme
  teslimat->sozlesme
  ucretlendirme->sozlesme
  sozlesme       degisirse etkilenen baglam = 2 (teslimat, ucretlendirme)
  ucretlendirme  degisirse etkilenen baglam = 2 (sozlesme, teslimat)
  teslimat       degisirse etkilenen baglam = 2 (sozlesme, ucretlendirme)
  toplam yayilma = 6
  yukari akis adi gecen dosya = 2 (adsiz/ucret.mjs, adsiz/teslimat.mjs)
  bunlardan cekirdek dosya = 2 / 2
adli: 6 dosya, 3 yonlu kenar, 0 karsilikli baglam cifti
  teslimat->sozlesme
  ucretlendirme->sozlesme
  ucretlendirme->teslimat
  sozlesme       degisirse etkilenen baglam = 2 (teslimat, ucretlendirme)
  ucretlendirme  degisirse etkilenen baglam = 0
  teslimat       degisirse etkilenen baglam = 1 (ucretlendirme)
  toplam yayilma = 3
  yukari akis adi gecen dosya = 2 (adli/ucretlendirme-ceviri.mjs, adli/teslimat-ceviri.mjs)
  bunlardan cekirdek dosya = 0 / 3
```

Adsız düzende üç bağlam arasında dört yönlü kenar var ve bunların ikisi karşılıklı çift: hem
sözleşme ücretlendirmeye bağımlı hem ücretlendirme sözleşmeye. Karşılıklılığı yaratan tek şey
raporun yeridir. Rapor sözleşme bağlamının içinde durduğu için o bağlam, kendisine bağımlı iki
bağlamı geri çağırmak zorunda kalıyor. Sonuç, üç bağlamın hepsinin birbirine bağlanması:
hangisi değişirse öteki ikisi etkileniyor, toplam yayılma 6.

Adlı düzende kenar sayısı üçe, karşılıklı çift sayısı sıfıra iniyor. Yukarı akıştaki sözleşme
bağlamı değiştiğinde iki bağlam hâlâ etkilenir — bu kaçınılmazdır, indirim oranını gerçekten o
bağlam tanımlıyor. Değişen şey ötekiler: ücretlendirme değişirse hiçbir bağlam etkilenmiyor,
teslimat değişirse bir bağlam etkileniyor. Toplam yayılma 6'dan 3'e düştü.

Son iki satır sızmayı gösteriyor. Yukarı akışın dört adı iki düzende de iki dosyada geçiyor;
sayı aynı. Fark, o iki dosyanın ne olduğudur. Adsız düzende ikisi de aşağı akış bağlamlarının
çekirdek dosyaları (2/2); adlı düzende ikisi de sınır dosyası ve çekirdek dosyaların hiçbirinde
yukarı akıştan tek ad geçmiyor (0/3). Sözleşme bağlamı `tarifeIndirimYuzdesi` yerine
`indirimOrani` demeye karar verirse iki düzende de iki dosya düzenlenir, ama adsız düzende
düzenlenen dosyalar iş kuralının kendisini taşıyan dosyalardır.

## Davranış Korundu mu

Haritanın değişmesi çıktının değişmesi değildir; sınanır.

```js
// kosum.mjs — iki duzen ayni raporu ve ayni teslim penceresini veriyor mu
import { sozlesmeOzeti as adsizOzet } from "./adsiz/rapor.mjs";
import { teslimPenceresiSaat as adsizPencere } from "./adsiz/teslimat.mjs";
import { sozlesmeOzeti as adliOzet } from "./adli/rapor.mjs";
import { teslimPenceresiSaat as adliPencere } from "./adli/teslimat.mjs";

const a = JSON.stringify(adsizOzet(10000)), b = JSON.stringify(adliOzet(10000));
console.log(`adsiz ${a}`);
console.log(`adli  ${b}`);
console.log(`rapor ayni mi = ${a === b}`);
let ayrilan = 0;
for (const no of ["S-77", "S-12", "S-99"]) {
  if (adsizPencere(no) !== adliPencere(no)) ayrilan += 1;
}
console.log(`ayrilan pencere = ${ayrilan} / 3`);
```

```sh
node kosum.mjs
```

```
adsiz [{"odeyen":"M-4","net":8624,"pencere":48},{"odeyen":"M-9","net":8000,"pencere":24}]
adli  [{"odeyen":"M-4","net":8624,"pencere":48},{"odeyen":"M-9","net":8000,"pencere":24}]
rapor ayni mi = true
ayrilan pencere = 0 / 3
```

Rapor birebir aynı, üç sözleşme numarasının teslim penceresi de aynı. Tanımsız sözleşme
numarası `S-99` dâhil. Harita yeniden çizildi, alan davranışı yerinde kaldı.

## Haritanın Bedeli

Adlı düzen iki dosya daha büyük ve her okumada bir dolaylılık katmanı fazla taşıyor. Bedelin
ikinci parçası daha az görünür: sınır dosyası yalnız çevirdiği kadarını geçirir. Sözleşme
bağlamı yeni bir alan yayımladığında aşağı akış onu doğrudan kullanamaz, önce çeviriye
eklenmesi gerekir. Bu, gecikme demektir; karşılığında iş kuralı dosyalarının yukarı akışın
sözlüğünü hiç tanımaması gelir.

Haritanın kendisinin de bir bedeli var: yazılmayan harita yoktur, yalnız görülmeyen harita
vardır. Adsız düzenin de bir haritası çıktı, dört kenarlı ve iki karşılıklı çiftli. Fark,
birinin yazılmış olmasıdır.

## Özet

- Bağlam haritası, bağlamlar arası ilişkilerin türünü ve yönünü yazıya geçirir; ok, alan
  kararını veren tarafa bakar ve bu, bağımlılık yönündeki kararlılık ölçütünden ayrı bir
  sorudur.
- İlişki türleri adlandırılır: ortaklık, müşteri–tedarikçi, açık sunucu servisi, yayımlanmış
  dil, ayrı yollar; adsız ilişkide bağ vardır ama yönü yazılmamıştır.
- Harita kodun gerçek ithallerinden çıkarıldığında adsız düzende 4 yönlü kenar ve 2 karşılıklı
  bağlam çifti göründü; karşılıklılığı yaratan şey raporun yukarı akış bağlamının içinde
  durmasıydı.
- İlişkiler adlandırıldığında kenar sayısı 3'e, karşılıklı çift sayısı 0'a indi; toplam yayılma
  6'dan 3'e düştü ve ücretlendirme değişirse etkilenen bağlam sayısı 2'den 0'a indi.
- Yukarı akışın dört adı iki düzende de iki dosyada geçti, ama adsız düzende bu dosyalar
  çekirdek dosyalardı (2/2), adlı düzende hepsi sınır dosyasıydı (çekirdekte 0/3).
- Rapor ve üç teslim penceresi iki düzende birebir aynı çıktı; bedel iki fazla dosya ve
  çevirinin yalnız eşlediği kadarını geçirmesidir.

## Sonraki Adım

Bu dersteki çeviri dosyaları kolay bir durumda çalıştı: yukarı akıştaki sözleşme bağlamı aynı
kod tabanının parçası, adları düzenli, tipleri belli. Kitaplığın gerçek dünyada konuştuğu
yukarı akışların bir kısmı böyle değil — taşıyıcı sağlayıcılarının kendi modelleri, kendi durum
kümeleri ve kendi kimlik biçimleri var; hiçbiri kitaplığın alan diline uymak zorunda değil ve
değiştiklerinde kimseye danışmıyor. Böyle bir yukarı akışın kavramları içeri sızarsa iç model
dışın diliyle konuşmaya başlar: teslimat bağlamının durumları taşıyıcının durum kodlarına
dönüşür. Sonraki ders dış modelin adlarının ve tiplerinin kaç dosyada göründüğünü sayar,
araya bir savunma katmanı koyduğunda bu sayının nereye indiğini ölçer ve katmanın bedelini
dosya ile satır cinsinden çıkarır.
