---
title: 'Fabrikalar ve Depolar'
source: 'https://academia.sh/tr/kurslar/alan-odakli-tasarim/fabrikalar-ve-depolar'
course: 'Alan Odaklı Tasarım'
language: tr
updated: '2026-08-17T18:10:41+00:00'
license: 'CC BY-SA 4.0'
---

# Fabrikalar ve Depolar

Yaratım ve erişimin alan modeline uydurulması: geçersiz bir gönderinin dört yaratım yolundan kaçında üretilebildiğinin sayılması, fabrikadan geçen modelde bu sayının sıfıra inmesi, depo arayüzü sorgu diliyle yazıldığında çağrı yerine sızan sütun adı, işleç ve eşik sayısının ölçülmesi ve iki deponun aynı sonucu verdiğinin gösterilmesi.

Önceki dersin uygulama servisi gönderiyi bir depodan okudu ve tarifeleri ondan istedi. O
deponun arayüzünde hangi sözcüklerin yazılı olduğu sorulmadı. Aynı soru yaratım için de
geçerlidir: gönderi doğrudan yapıcıyla mı kuruluyor, yoksa değişmezini kuruluş anında
güvenceye alan bir yerden mi geçiyor?

**Fabrika** (factory) ve **depo** (repository) kalıpları Tasarım Kalıpları kursunda kuruldu;
biri yaratım sorumluluğunun nereye konduğunu, öteki toplamaya erişimin nereden geçtiğini
söylüyordu. Bu ders kalıpları yeniden anlatmaz. Tek soruyu sorar: ikisi de **alan diliyle mi**
yazılmış? Fabrika için bu, değişmezin kuruluş anında sağlanması demektir; depo için, arayüzde
alan sözcüklerinin bulunması ve sütun adlarının bulunmaması demektir.

## Geçerlilik Kuralı

İki model de aynı kurala tabi tutulur: gönderi numarası boş olmayan bir katardır, ağırlık
pozitif bir tam sayıdır, bölge kodu iki basamaklıdır.

```sh
mkdir -p ortak sorgu alan
```

```js
// ortak/kural.mjs — iki modelin de tabi oldugu gecerlilik kurali
export const gecerliMi = (g) =>
  typeof g.gonderiNo === "string" && g.gonderiNo.length > 0
  && Number.isInteger(g.agirlikGram) && g.agirlikGram > 0
  && /^\d{2}$/.test(g.bolgeKodu);
```

## Yaratım: Kural Nerede Uygulanıyor

Birinci sürümde yapıcı dışa açıktır, alanlar yazılabilir ve kural hiçbir yerde uygulanmaz.

```js
// sorgu/gonderi.mjs — yapici disa acik, alanlar yazilabilir, kural kurulus aninda yok
export class Gonderi {
  constructor(gonderiNo, agirlikGram, bolgeKodu, sozlesmeNo = null) {
    this.gonderiNo = gonderiNo;
    this.agirlikGram = agirlikGram;
    this.bolgeKodu = bolgeKodu;
    this.sozlesmeNo = sozlesmeNo;
  }
}

export const kayittanKur = (satir) =>
  new Gonderi(satir.gonderi_no, satir.agirlik_gram, satir.bolge_kodu, satir.sozlesme_no);
```

İkinci sürümde yapıcıya yalnız fabrikanın bildiği bir belirteçle girilir. İki fabrika yöntemi
vardır — biri alan çağrıları için, biri ham kayıttan kurmak için — ve ikisi de aynı kuralı
uygular. Nesne kuruluşta dondurulur.

```js
// alan/gonderi.mjs — fabrika: gecerlilik kurulus aninda saglanir, nesne dondurulur
import { gecerliMi } from "../ortak/kural.mjs";

const FABRIKA = Symbol("fabrika");

export class Gonderi {
  constructor(anahtar, gonderiNo, agirlikGram, bolgeKodu, sozlesmeNo) {
    if (anahtar !== FABRIKA) throw new TypeError("Gonderi yalnizca fabrikadan kurulur");
    this.gonderiNo = gonderiNo;
    this.agirlikGram = agirlikGram;
    this.bolgeKodu = bolgeKodu;
    this.sozlesmeNo = sozlesmeNo;
    Object.freeze(this);
  }
  static olustur(gonderiNo, agirlikGram, bolgeKodu, sozlesmeNo = null) {
    const aday = { gonderiNo, agirlikGram, bolgeKodu, sozlesmeNo };
    if (!gecerliMi(aday)) throw new RangeError(`gecersiz gonderi: ${gonderiNo}`);
    return new Gonderi(FABRIKA, gonderiNo, agirlikGram, bolgeKodu, sozlesmeNo);
  }
  static kayittanKur(satir) {
    return Gonderi.olustur(satir.gonderi_no, satir.agirlik_gram, satir.bolge_kodu,
      satir.sozlesme_no);
  }
}
```

Ölçü, önceki dersteki ulaşılabilirlik ölçüsünün yaratım tarafındaki karşılığıdır: geçersiz bir
gönderi dört yoldan kaçında üretilebiliyor?

```js
// fabrika-olc.mjs — gecersiz bir gonderi kac yoldan yaratilabiliyor
import { gecerliMi } from "./ortak/kural.mjs";
import { Gonderi as SorguGonderi, kayittanKur } from "./sorgu/gonderi.mjs";
import { Gonderi as AlanGonderi } from "./alan/gonderi.mjs";

const BOZUK_SATIR = { gonderi_no: "G-4099", agirlik_gram: -50, bolge_kodu: "3",
  sozlesme_no: null };

const SORGU_YOLLARI = {
  "yapicidan dogrudan": () => new SorguGonderi("", -3, "3"),
  "alanin sonradan atanmasi": () => {
    const g = new SorguGonderi("G-4098", 2400, "34");
    g.agirlikGram = -3;
    return g;
  },
  "toplu atama": () => Object.assign(new SorguGonderi("G-4098", 2400, "34"),
    { bolgeKodu: "abc" }),
  "ham kayittan kurma": () => kayittanKur(BOZUK_SATIR),
};
const ALAN_YOLLARI = {
  "yapicidan dogrudan": () => new AlanGonderi("", -3, "3"),
  "alanin sonradan atanmasi": () => {
    const g = AlanGonderi.olustur("G-4098", 2400, "34");
    g.agirlikGram = -3;
    return g;
  },
  "toplu atama": () => Object.assign(AlanGonderi.olustur("G-4098", 2400, "34"),
    { bolgeKodu: "abc" }),
  "ham kayittan kurma": () => AlanGonderi.kayittanKur(BOZUK_SATIR),
};

for (const [ad, yollar] of Object.entries({ sorgu: SORGU_YOLLARI, alan: ALAN_YOLLARI })) {
  let gecersiz = 0;
  let hata = 0;
  for (const [yol, dene] of Object.entries(yollar)) {
    let sonuc;
    try {
      const g = dene();
      sonuc = gecerliMi(g) ? "gecerli" : "GECERSIZ NESNE";
      if (sonuc !== "gecerli") gecersiz += 1;
    } catch {
      sonuc = "hata";
      hata += 1;
    }
    console.log(`  ${ad.padEnd(6)} ${yol.padEnd(26)} ${sonuc}`);
  }
  console.log(`${ad}: 4 yoldan ${gecersiz} tanesi gecersiz nesne uretti, ${hata} tanesi hata verdi`);
}
```

```sh
node fabrika-olc.mjs
```

```
  sorgu  yapicidan dogrudan         GECERSIZ NESNE
  sorgu  alanin sonradan atanmasi   GECERSIZ NESNE
  sorgu  toplu atama                GECERSIZ NESNE
  sorgu  ham kayittan kurma         GECERSIZ NESNE
sorgu: 4 yoldan 4 tanesi gecersiz nesne uretti, 0 tanesi hata verdi
  alan   yapicidan dogrudan         hata
  alan   alanin sonradan atanmasi   hata
  alan   toplu atama                hata
  alan   ham kayittan kurma         hata
alan: 4 yoldan 0 tanesi gecersiz nesne uretti, 4 tanesi hata verdi
```

Dört yolun dördü birinci modelde geçersiz bir gönderi üretiyor ve hiçbiri uyarı vermiyor.
Fabrikadan geçen modelde dördü de hata veriyor. Kazanç kuralın kaç kez yazıldığı değildir —
kural iki modelde de bir kez yazılabilirdi — kuralı **atlayan bir yolun kalmamasıdır**.

## Erişim: Depo Hangi Dili Konuşuyor

Kalıcılık tarafından gelen ham satırlar sütun adlarını taşır.

```js
// ortak/satirlar.mjs — kalicilik tarafindan gelen ham satirlar
export const SATIRLAR = [
  { gonderi_no: "G-4001", agirlik_gram: 2400, bolge_kodu: "34", sozlesme_no: "S-77" },
  { gonderi_no: "G-4002", agirlik_gram: 7200, bolge_kodu: "06", sozlesme_no: null },
  { gonderi_no: "G-4003", agirlik_gram: 400, bolge_kodu: "65", sozlesme_no: "S-77" },
  { gonderi_no: "G-4004", agirlik_gram: 12000, bolge_kodu: "34", sozlesme_no: null },
  { gonderi_no: "G-4005", agirlik_gram: 5600, bolge_kodu: "06", sozlesme_no: "S-77" },
];
```

Birinci depo genel bir sorgu arayüzü verir: koşulu çağıran taraf kurar ve ham satır alır.

```js
// sorgu/depo.mjs — sorgu diliyle yazilmis depo: kosulu cagiran taraf kurar
const KARS = {
  "=": (a, b) => a === b,
  ">": (a, b) => a > b,
  "<": (a, b) => a < b,
};

export const depo = (satirlar) => ({
  bul: (kosul) => satirlar.filter((s) => kosul.every(([alan, isl, d]) => KARS[isl](s[alan], d))),
});
```

```js
// sorgu/cagrilar.mjs — sutun adlari, isleciler ve esikler cagri yerinde yazili
export const agirGonderiler = (d) => d.bul([["agirlik_gram", ">", 5000]]);
export const bolgedekiler = (d, bolge) => d.bul([["bolge_kodu", "=", bolge]]);
export const sozlesmesizAgirlar = (d) =>
  d.bul([["agirlik_gram", ">", 5000], ["sozlesme_no", "=", null]]);
export const bolgeAgirliklari = (d, bolge) =>
  d.bul([["bolge_kodu", "=", bolge]]).map((s) => s.agirlik_gram);
export const hafifSozlesmeliler = (d) =>
  d.bul([["agirlik_gram", "<", 1000], ["sozlesme_no", "=", "S-77"]]);
```

İkinci depo alan sözcüklerini yöntem adı olarak verir, ham satırı fabrikadan geçirir ve alan
nesnesi döndürür. "Ağır" ile "hafif" eşiklerinin ne olduğu da burada durur.

```js
// alan/depo.mjs — alan diliyle yazilmis depo: sutun adi, esik ve islec burada kalir
import { Gonderi } from "./gonderi.mjs";

const AGIR_SINIR_GRAM = 5000;
const HAFIF_SINIR_GRAM = 1000;
const kur = (satirlar) => satirlar.map((s) => Gonderi.kayittanKur(s));

export const depo = (satirlar) => ({
  agirGonderiler: () => kur(satirlar.filter((s) => s.agirlik_gram > AGIR_SINIR_GRAM)),
  bolgedekiler: (bolge) => kur(satirlar.filter((s) => s.bolge_kodu === bolge)),
  sozlesmesizAgirlar: () =>
    kur(satirlar.filter((s) => s.agirlik_gram > AGIR_SINIR_GRAM && s.sozlesme_no === null)),
  hafifSozlesmeliler: (sozlesmeNo) => kur(satirlar.filter((s) =>
    s.agirlik_gram < HAFIF_SINIR_GRAM && s.sozlesme_no === sozlesmeNo)),
});
```

```js
// alan/cagrilar.mjs — cagri yerinde yalniz alan sozcukleri var
export const agirGonderiler = (d) => d.agirGonderiler();
export const bolgedekiler = (d, bolge) => d.bolgedekiler(bolge);
export const sozlesmesizAgirlar = (d) => d.sozlesmesizAgirlar();
export const bolgeAgirliklari = (d, bolge) => d.bolgedekiler(bolge).map((g) => g.agirlikGram);
export const hafifSozlesmeliler = (d) => d.hafifSozlesmeliler("S-77");
```

```js
// depo-olc.mjs — sutun adi ve islec cagri yerine siziyor mu, iki depo ayni sonucu veriyor mu
import { readFileSync } from "node:fs";
import { SATIRLAR } from "./ortak/satirlar.mjs";
import { depo as sorguDepo } from "./sorgu/depo.mjs";
import { depo as alanDepo } from "./alan/depo.mjs";
import * as sorguCagri from "./sorgu/cagrilar.mjs";
import * as alanCagri from "./alan/cagrilar.mjs";

const SUTUN = /agirlik_gram|bolge_kodu|sozlesme_no/g;
const ISLEC = /"[=<>]"/g;
const ESIK = /5000|1000/g;
const MODEL = {
  sorgu: { cagri: "sorgu/cagrilar.mjs", sinir: ["sorgu/depo.mjs"] },
  alan: { cagri: "alan/cagrilar.mjs", sinir: ["alan/depo.mjs", "alan/gonderi.mjs"] },
};

for (const [ad, { cagri, sinir }] of Object.entries(MODEL)) {
  const metin = readFileSync(cagri, "utf8");
  const say = (r) => (metin.match(r) ?? []).length;
  const sinirda = sinir.map((d) => (readFileSync(d, "utf8").match(SUTUN) ?? []).length)
    .reduce((a, b) => a + b, 0);
  console.log(`${ad}: cagri yerinde sutun adi ${say(SUTUN)}, islec ${say(ISLEC)}, ` +
    `esik ${say(ESIK)}`);
  console.log(`  sinir dosyalarinda sutun adi ${sinirda} [${sinir.join(" ")}]`);
}

const s = sorguDepo(SATIRLAR);
const a = alanDepo(SATIRLAR);
const ad = (liste) => liste.map((x) => x.gonderi_no ?? x.gonderiNo ?? x).join(",");
let ayrilan = 0;
for (const [islem, x, y] of [
  ["agirGonderiler", sorguCagri.agirGonderiler(s), alanCagri.agirGonderiler(a)],
  ["bolgedekiler", sorguCagri.bolgedekiler(s, "34"), alanCagri.bolgedekiler(a, "34")],
  ["sozlesmesizAgirlar", sorguCagri.sozlesmesizAgirlar(s), alanCagri.sozlesmesizAgirlar(a)],
  ["bolgeAgirliklari", sorguCagri.bolgeAgirliklari(s, "06"), alanCagri.bolgeAgirliklari(a, "06")],
  ["hafifSozlesmeliler", sorguCagri.hafifSozlesmeliler(s), alanCagri.hafifSozlesmeliler(a)],
]) {
  if (ad(x) !== ad(y)) ayrilan += 1;
  console.log(`  ${islem.padEnd(20)} ${ad(x)}`);
}
console.log(`ayrilan sonuc = ${ayrilan} / 5`);
```

```sh
node depo-olc.mjs
```

```
sorgu: cagri yerinde sutun adi 8, islec 7, esik 3
  sinir dosyalarinda sutun adi 0 [sorgu/depo.mjs]
alan: cagri yerinde sutun adi 0, islec 0, esik 0
  sinir dosyalarinda sutun adi 9 [alan/depo.mjs alan/gonderi.mjs]
  agirGonderiler       G-4002,G-4004,G-4005
  bolgedekiler         G-4001,G-4004
  sozlesmesizAgirlar   G-4002,G-4004
  bolgeAgirliklari     7200,5600
  hafifSozlesmeliler   G-4003
ayrilan sonuc = 0 / 5
```

Sorgu diliyle yazılmış depoda çağrı yeri sekiz sütun adı, yedi karşılaştırma işleci ve üç eşik
sayısı taşıyor. Alan diliyle yazılmış depoda üçü de sıfır. Sütun adları yok olmadı; dokuz
geçişle iki sınır dosyasına toplandı.

Toplanmanın karşılığı üç ayrı karar cinsindedir. Sütun adı bir kalıcılık kararıdır; işleç bir
sorgu kararıdır; "ağır" eşiği bir **alan** kararıdır. Sorgu modelinde üçü de aynı satırda,
uygulama katmanının içinde duruyor. Eşiğin 5000 gramdan 4000 grama inmesi orada iki çağrı
yerine dokunmayı gerektirir ve o iki yer alan sözlüğünde "ağır" diye bir kavram bulunmadığı
için aranamaz. Alan modelinde aynı değişiklik `AGIR_SINIR_GRAM` sabitine dokunur.

Son satır ölçünün ne olmadığını yine söylüyor: beş sorgunun beşinde iki depo aynı gönderileri
döndürüyor.

## Deponun Alan Diliyle Yazılmasının Sınırı

Alan diliyle yazılmış deponun bedeli yöntem sayısıdır. Her yeni alan sorusu yeni bir yöntem
açar ve arayüz büyür; genel sorgu arayüzünde arayüz tek yöntemde kalır, büyüyen şey çağrı
yerlerindeki koşullardır.

Ölçü bu yüzden soruların **tekrarına** bakar. Aynı soru birden çok yerden soruluyorsa
yöntemleşmesi kazanç üretir. Yalnız bir yerden sorulan, bir daha sorulmayacak sorularda —
bir kereye mahsus bir mutabakat raporu gibi — genel arayüz daha ucuzdur; sorgu nesneleri ve
belirtimler kalıbı Veri Erişim Katmanı ve İş Mantığı kursunda tam bu durum için kurulmuştu.

İkinci sınır fabrikadadır. Fabrikanın kazancı, atlanacak yol bırakmamasından gelir; bu ise
yapıcıya erişimi kapatmayı ve nesneyi dondurmayı gerektirir. Değişmezi olmayan bir nesnede
bu bedelin karşılığı yoktur: dondurmak her güncellemede yeni nesne üretmek demektir ve kuralı
olmayan bir nesnede korunacak bir şey de yoktur.

## Özet

- Fabrika ve depo bu derste kalıp olarak değil, alan diline uyup uymadıklarıyla ele alındı.
- Yapıcının dışa açık olduğu modelde geçersiz bir gönderi dört yaratım yolunun 4'ünde üretildi
  ve hiçbiri uyarı vermedi; fabrikadan geçen modelde 0'ında üretildi, dördü hata verdi.
- Fabrikanın kazancı kuralın kaç kez yazıldığı değil, kuralı atlayan yolun kalmamasıdır.
- Sorgu diliyle yazılmış deponun çağrı yeri 8 sütun adı, 7 işleç ve 3 eşik taşıyordu; alan
  diliyle yazılmış deponun çağrı yerinde üçü de 0, sütun adları iki sınır dosyasında toplandı.
- Sorgu modelinde kalıcılık, sorgu ve alan kararları aynı satırda karışıyor; "ağır" eşiği bir
  alan kararı olduğu hâlde alan sözlüğünde adı bulunmuyor.
- Beş sorgunun beşinde iki depo aynı sonucu verdi; alan diliyle yazılmış deponun bedeli
  arayüzün soru sayısıyla büyümesidir.

## Sonraki Adım

Buraya kadarki bütün ölçüler modelin **durumu** üzerineydi: hangi ad hangi kavramı gösteriyor,
hangi değişmez hangi sınırın içinde, nesne hangi yolla kuruluyor. Alanda bir de olup bitenler
var: bir gönderi yeniden fiyatlandırıldı, sözleşme indirimi kaldırıldı, tarife değiştiği için
tutar yeniden hesaplandı. Bu olguların kodda bir adı olabilir ya da yalnız "kayıt güncellendi"
diye geçebilirler. Sonraki ders bir olay kütüğü üzerinde alan uzmanının sorularını yanıtlamayı
dener: olay adları alan dilinden geldiğinde kaç soru tek süzmeyle yanıtlanıyor, geldiği yerde
kaç olayın alanları karşılaştırılmak zorunda kalıyor.
