İçeriğe geç
academia.sh

Ders 26 / 30

Depo ve İş Birimi

Bir arada tutulan nesne kümesinin erişim ve yazma sınırı: elle eşleyici çağıran uygulama katmanı ile toplamayı alan diliyle veren depo ve değişen satırları tek noktada toplayan iş birimi, üç düzende yazma çağrısı, yazma noktası ve arama çağrısı sayılarının karşılaştırılması.

İçindekiler

Önceki dersin eşleyicisi tek bir nesneyi tek bir satıra çeviriyordu. Gerçek bir senaryo tek nesneye dokunmaz: bir gönderi kaydedilirken rotası ve indirim satırları da yazılır, gönderi satırındaki indirim toplamı da yenilenir. Bu üç satır birbirinden bağımsız değildir; bir arada anlam taşıyan bir küme oluşturur ve bu kümeye toplama (aggregate) adı verilir.

Toplamanın varlığı iki soru doğurur. Birincisi erişim sorusu: çağıran taraf toplamanın parçalarını tek tek mi arayacak, bütününü mü isteyecek. İkincisi yazma sorusu: hangi satırların hangi sırada yazılacağını kim bilecek. Birinci soruyu depo (repository), ikincisini iş birimi (unit of work) yanıtlar. İki kalıp da Veri Erişim Katmanı kursunda kurulmuştu; işlem sınırı, yalıtım düzeyi ve kilitleme davranışı orada ölçüldü. Burada ölçü bir tasarım ölçüsüdür: uygulama katmanının taşıdığı kalıcılık adı sayısı, yazma noktası sayısı ve aynı senaryonun ürettiği yazma çağrısı sayısı.

mkdir -p esleyici elle depo
// bellek-depo.mjs — sade nesne deposu; yapilan yazmalari da sayar
const TABLOLAR = new Map();
let yazma = 0;
const tablo = (ad) => TABLOLAR.get(ad) ?? TABLOLAR.set(ad, new Map()).get(ad);

export const depo = {
  oku: (ad, id) => tablo(ad).get(id) ?? null,
  yaz: (ad, satir) => { yazma += 1; tablo(ad).set(satir.id, { ...satir }); },
  yazmaSayisi: () => yazma,
  sifirla: () => { yazma = 0; },
};
// esleyici/tablo-esleyici.mjs — tablo basina ayni cevrimi kuran kucuk esleyici
import { depo } from "../bellek-depo.mjs";

export const esleyici = (tablo) => ({
  bul: (id) => depo.oku(tablo, id),
  kaydet: (satir) => depo.yaz(tablo, satir),
});

Elle Kaydetme

İlk düzende uygulama katmanı üç eşleyiciyi kendisi kullanır. Her adımda hangi tabloların değiştiğine de kendisi karar verir.

// elle/senaryo.mjs — uygulama katmani hangi tabloyu hangi sirada yazacagini kendi bilir
import { esleyici } from "../esleyici/tablo-esleyici.mjs";

const gonderiE = esleyici("gonderi"), rotaE = esleyici("rota"), indirimE = esleyici("indirim");
const ozet = (t) => ({ ...t.gonderi, indirimToplami: t.indirimler.reduce((s, i) => s + i.oran, 0) });

export function olustur(toplama) {
  gonderiE.kaydet(ozet(toplama));
  rotaE.kaydet(toplama.rota);
  for (const i of toplama.indirimler) indirimE.kaydet(i);
  return toplama;
}

export function aktarmaEkle(toplama, nokta) {
  toplama.rota.aktarma.push(nokta);
  rotaE.kaydet(toplama.rota);
  return toplama;
}

export function indirimEkle(toplama, indirim) {
  toplama.indirimler.push(indirim);
  indirimE.kaydet(indirim);
  gonderiE.kaydet(ozet(toplama));
  return toplama;
}

export function topla(id) {
  const gonderi = gonderiE.bul(id);
  const rota = rotaE.bul(`R-${id}`);
  const indirimler = [indirimE.bul(`I-${id}`)].filter((x) => x !== null);
  return { gonderi, rota, indirimler };
}

Bu dosyada iki tür bilgi var: senaryonun sırası ve kalıcılığın yapısı. İkinci tür bilgi üçüncü kez yazılan bir kararı içeriyor — indirim eklendiğinde gönderi satırının da yenileneceği kararı.

Depo

Depo, çağıran tarafa toplamanın bütününü verir. Yazma işini ise doğrudan yapmaz, kendisine verilen bir yazıcıya devreder; bu, iş biriminin sonradan araya girmesini mümkün kılar.

// depo/gonderi-deposu.mjs — toplamanin tamamini alan diliyle veren depo
import { depo } from "../bellek-depo.mjs";

const ozet = (t) => ({ ...t.gonderi, indirimToplami: t.indirimler.reduce((s, i) => s + i.oran, 0) });

export const gonderiDeposu = (yazici) => ({
  bul(id) {
    const gonderi = depo.oku("gonderi", id);
    if (gonderi === null) return null;
    const indirimler = [depo.oku("indirim", `I-${id}`)].filter((x) => x !== null);
    return { gonderi, rota: depo.oku("rota", `R-${id}`), indirimler };
  },
  kaydet(toplama) {
    yazici("gonderi", ozet(toplama));
    yazici("rota", toplama.rota);
    for (const i of toplama.indirimler) yazici("indirim", i);
    return toplama;
  },
});
// depo/senaryo.mjs — ayni senaryo: uygulama katmani yalniz toplamayi kaydeder
import { gonderiDeposu } from "./gonderi-deposu.mjs";

export const senaryo = (yazici) => {
  const kaynak = gonderiDeposu(yazici);
  return {
    olustur: (toplama) => kaynak.kaydet(toplama),
    aktarmaEkle(toplama, nokta) {
      toplama.rota.aktarma.push(nokta);
      return kaynak.kaydet(toplama);
    },
    indirimEkle(toplama, indirim) {
      toplama.indirimler.push(indirim);
      return kaynak.kaydet(toplama);
    },
    topla: (id) => kaynak.bul(id),
  };
};

Senaryo dosyasında tablo adı kalmadı ve her adım tek bir kaydetme çağrısıyla bitiyor. Karşılığında depo her kaydetmede toplamanın bütününü yazıyor: değişmeyen satırları da.

İş Birimi

İş birimi bu iki uçtan üçüncü bir yol açar. Yazmaları anında yapmak yerine hangi satırların kirlendiğini kaydeder, sonunda her satırı bir kez yazar.

// depo/is-birimi.mjs — degisen satirlari izler, yazmayi tek noktada toplar
import { depo } from "../bellek-depo.mjs";

export function isBirimi() {
  const kirli = new Map();
  return {
    kirlet: (tablo, satir) => { kirli.set(`${tablo}:${satir.id}`, { tablo, satir }); },
    izlenen: () => kirli.size,
    tamamla() {
      for (const { tablo, satir } of kirli.values()) depo.yaz(tablo, satir);
      const n = kirli.size;
      kirli.clear();
      return n;
    },
  };
}

Anahtar tablo:kimlik çiftidir; aynı satır üç kez kirlense de haritada bir kez durur. Deponun yazıcısı olarak kirlet verildiğinde depo kodu hiç değişmeden yazma davranışı değişir.

Üç Düzenin Sayılması

Aynı üç adım — toplama oluştur, rotaya aktarma noktası ekle, indirim ekle — üç düzende koşturulur ve iki tür sayı çıkarılır: koşumda yapılan yazma çağrısı, kaynak dosyalarda duran tablo adı, yazma noktası ve arama çağrısı sayısı.

// yazma-say.mjs — ayni uc adim uc duzende: yazma cagrisi, yazma noktasi ve arama cagrisi
import { readFileSync } from "node:fs";
import { depo } from "./bellek-depo.mjs";
import * as elle from "./elle/senaryo.mjs";
import { senaryo } from "./depo/senaryo.mjs";
import { isBirimi } from "./depo/is-birimi.mjs";

const yeni = (id) => ({
  gonderi: { id, agirlik: 3, katsayi: 1.35 },
  rota: { id: `R-${id}`, gonderiId: id, aktarma: ["34"] },
  indirimler: [],
});
const indirim = (id) => ({ id: `I-${id}`, gonderiId: id, ad: "sozlesme", oran: 0.15 });
const okuyucu = senaryo(() => {});

function olc(ad, id, adimlar) {
  depo.sifirla();
  const ek = adimlar(id);
  const t = okuyucu.topla(id);
  console.log(`${ad}: yazma cagrisi = ${depo.yazmaSayisi()}, indirim toplami = ${t.gonderi.indirimToplami}${ek}`);
}

olc("elle esleyici       ", "G1", (id) => {
  elle.indirimEkle(elle.aktarmaEkle(elle.olustur(yeni(id)), "06"), indirim(id));
  return "";
});

olc("depo, dogrudan yazma", "G2", (id) => {
  const s = senaryo(depo.yaz);
  s.indirimEkle(s.aktarmaEkle(s.olustur(yeni(id)), "06"), indirim(id));
  return "";
});

olc("depo ve is birimi   ", "G3", (id) => {
  const birim = isBirimi();
  const s = senaryo(birim.kirlet);
  s.indirimEkle(s.aktarmaEkle(s.olustur(yeni(id)), "06"), indirim(id));
  return `, izlenen kirli satir = ${birim.izlenen()}, tamamla() yazdi = ${birim.tamamla()}`;
});

const SAY = { "tablo adi": /"(gonderi|rota|indirim)"/g, "yazma noktasi": /\b(kaydet|kirlet)\(/g, "arama cagrisi": /\.bul\(/g };
for (const dosya of ["elle/senaryo.mjs", "depo/senaryo.mjs"]) {
  const m = readFileSync(dosya, "utf8");
  const alanlar = Object.entries(SAY).map(([k, r]) => `${k} = ${[...m.matchAll(r)].length}`);
  console.log(`${dosya}: ${alanlar.join(", ")}`);
}
node yazma-say.mjs
elle esleyici       : yazma cagrisi = 5, indirim toplami = 0.15
depo, dogrudan yazma: yazma cagrisi = 7, indirim toplami = 0.15
depo ve is birimi   : yazma cagrisi = 3, indirim toplami = 0.15, izlenen kirli satir = 3, tamamla() yazdi = 3
elle/senaryo.mjs: tablo adi = 3, yazma noktasi = 6, arama cagrisi = 3
depo/senaryo.mjs: tablo adi = 0, yazma noktasi = 3, arama cagrisi = 1

Üç düzen aynı indirim toplamını okudu: davranış değişmedi. Değişen sayılar şunlar.

Uygulama katmanının taşıdığı kalıcılık bilgisi depoyla birlikte silindi: tablo adı 3’ten 0’a, yazma noktası 6’dan 3’e, arama çağrısı 3’ten 1’e indi. Toplamanın parçalarını bulmak için uygulama katmanının bildiği kimlik biçimi — rotanın R- önekiyle türetildiği bilgisi — artık deponun içinde duruyor.

Yazma çağrısı sayısı beklenenin tersine hareket etti. Elle düzen 5, depo 7 yazma üretti; çünkü depo her adımda toplamanın tamamını yazıyor, değişmemiş rota satırını da. İş birimi eklendiğinde sayı 3’e indi: üç kirli satır, üç yazma. Depo tek yazma noktası kazandırır, iş birimi o noktanın kaç kez tetiklendiğini düşürür. İkisi ayrı problem çözer ve birlikte kullanıldıklarında sayı hem tek noktaya iner hem en aza düşer.

Bedel ve Ne Zaman Uygulanmaz

Depo 2 dosya ekler ve toplamaya erişim için 1 dolaylılık basamağı koyar; iş birimi 1 dosya daha ekler ve yazmanın ne zaman gerçekleştiğini çağrı noktasından kaldırır. Bu ikinci bedel sayının içinde görünmez: kaydet çağrıldığında hiçbir şey yazılmadığı için hata tamamla satırında ortaya çıkar ve izlenmesi gereken çağrı zinciri iki basamak uzar.

Depo, toplaması olmayan bir kayıt için karşılıksızdır. Tek tablodan oluşan bir arama kaydında depo yalnız eşleyiciyi başka bir adla sarar; ölçüm o durumda tablo adı 3’ten 0’a değil, 1’den 0’a iner ve karşılığında 1 dosya ödenir. Aynı şekilde raporlama okumaları toplama sınırına uymaz: bir depo arayüzü altı farklı süzgeç kombinasyonunu karşılamak için altı yöntemle büyür. Bu sınır Veri Erişim Katmanı kursundaki sorgu nesneleri ve belirtimler dersinin konusudur.

İş birimi ise tek satır yazan senaryolarda ölçüyü iyileştirmez: kirli satır sayısı 1 olur, yazma sayısı zaten 1’dir, kazanç 0 kalır. Kazancı satır sayısıyla ve aynı satıra dokunan adım sayısıyla büyür.

Özet

  • Toplama, bir arada anlam taşıyan satır kümesidir; depo bu kümeye erişimi alan diliyle verir, iş birimi kümedeki değişimlerin ne zaman yazılacağını yönetir.
  • Depo uygulandığında uygulama katmanında tablo adı 3’ten 0’a, yazma noktası 6’dan 3’e, arama çağrısı 3’ten 1’e indi; kimlik türetme kuralı deponun içine geçti.
  • Aynı üç adım elle düzende 5, depoyla 7, depo ve iş birimiyle 3 yazma çağrısı üretti; depo tek yazma noktası kazandırır, iş birimi tetiklenme sayısını düşürür.
  • Üç düzen aynı indirim toplamını okudu; kalıplar davranışı değiştirmedi, kalıcılık kararının hangi dosyada durduğunu ve kaç kez tetiklendiğini değiştirdi.
  • Toplaması olmayan tek tablolu kayıtta depo bir sarmalayıcıya, tek satır yazan senaryoda iş birimi kazançsız bir dolaylılığa dönüşür.

Sonraki Adım

İş birimi kirli satırları tablo:kimlik anahtarıyla topladı; aynı satır üç kez kirlense de bir kez yazıldı. Bu, yazma tarafında çözülmüş bir tekillik sorunudur. Okuma tarafında aynı sorun çözülmemiş durumda: aynı gönderi kimliği iki kez arandığında iki ayrı nesne doğar, biri değiştirilir, öteki eski değerlerle elde kalır ve sonra yazılırsa ilkini ezer. Sonraki ders aynı kimliğin tek nesneyle temsil edilmesini sağlayan kimlik haritasını kurar, üretilen ayrı örnek sayısını ve kimliğe göre eşitlik yazmak zorunda kalınan yer sayısını sayar.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat