Ders 28 / 30
Veri Aktarım Nesneleri ve Eşleyiciler
Katmanlar arası veri taşımanın kalıp düzeyinde ele alınması: iç modelin biçimi değiştiğinde doğrudan okuyan istemcilerin bozulmasıyla eşleyici arkasındaki istemcilerin bozulmamasının sayılması, istemci kodundaki iç alan adı sayısı ve eşlemenin alan başına satır bedeli.
İçindekiler
Buraya kadarki dört kalıp iş mantığı ile kalıcılık arasındaki sınırı düzenledi. Alan nesnesi kuralı taşıyor, kalıcılığı bilmiyor ve bellekte tekil. Bu nesne kitaplığın dışına da çıkacak: bir arayüz onu listeleyecek, bir fatura dökümü ondan üretilecek, bir kütük satırı onu kaydedecek. Nesnenin dışarıya olduğu gibi verilmesi iç yapıyı dış sözleşme hâline getirir.
Veri aktarım nesnesi (data transfer object) bu sözleşmeyi ayrı bir düz kayıt olarak tanımlar; eşleyici iç modelden o kaydı üretir. Kalıp Veri Erişim Katmanı kursunda kurulmuş ve orada dış sözleşmeye sızan alan sayısıyla ölçülmüştü. Burada ölçü değişim tarafındadır: iç modelin biçimi değiştiğinde kaç istemci çıktısı bozuluyor, istemci kodu kaç iç alan adı taşıyor, eşleme alan başına kaç satıra mal oluyor.
Problem: İç Biçim Dışarıdan Okunursa
İç model aynı bilgiyi iki farklı biçimde tutabilir. İkinci sürümde ücretle ilgili alanlar iç içe bir nesneye taşınmış durumda; taşınan bilgi aynı, adres farklı.
// alan.mjs — ayni bilginin iki ic modeli: ikincisinde ucret alanlari ic ice tasindi export const surumBir = () => ({ id: "G7", agirlik: 3, postaKodu: "06800", notlar: "sozlesmeli musteri", ham: 8640, indirimler: [{ ad: "sozlesme", oran: 0.15 }, { ad: "hacim", oran: 0.1 }], }); export const surumIki = () => ({ id: "G7", agirlik: 3, postaKodu: "06800", notlar: "sozlesmeli musteri", ucret: { taban: 8640, indirimler: [{ ad: "sozlesme", oran: 0.15 }, { ad: "hacim", oran: 0.1 }] }, });
Üç istemci bu nesneyi doğrudan okur ve kendi biçimini üretir.
// dtosuz/istemciler.mjs — uc istemci ic modelin alanlarini dogrudan okuyup bicimler const TAVAN = 0.4; const oran = (g) => Math.min(g.indirimler.reduce((t, i) => t + i.oran, 0), TAVAN); const net = (g) => Math.round(g.ham * (1 - oran(g))); export const liste = (g) => `${g.id} ${g.agirlik} kg ${net(g)}`; export const fatura = (g) => `${g.id}: taban ${g.ham}, ${g.indirimler.map((i) => `${i.ad} ${-Math.round(g.ham * i.oran)}`).join(", ")} = ${net(g)}`; export const kutuk = (g) => `${g.id} net=${net(g)} indirim=${g.indirimler.length}`;
Üç istemcinin üçü de ham ve indirimler adlarını biliyor. İç modelin bu iki adı taşıdığı
varsayımı böylece üç ayrı yere yayılmış oldu.
Çözüm: Sözleşmeyi Ayrı Kayıt Olarak Tanımlamak
Eşleyici, iç modelden dış sözleşmeyi üretir. İki iç sürüm için iki üretim işlevi vardır ama ürettikleri kayıtlar aynıdır.
// dto/esleyici.mjs — ic modelin her surumu icin ayni uc veri aktarim nesnesini uretir const TAVAN = 0.4; const kur = (id, agirlik, taban, indirimler) => { const oran = Math.min(indirimler.reduce((t, i) => t + i.oran, 0), TAVAN); const net = Math.round(taban * (1 - oran)); return { liste: { kimlik: id, agirlik, net }, fatura: { kimlik: id, taban, kalemler: indirimler.map((i) => ({ ad: i.ad, tutar: -Math.round(taban * i.oran) })), toplam: net }, kutuk: { kimlik: id, net, indirimSayisi: indirimler.length }, }; }; export const esleyiciBir = (g) => kur(g.id, g.agirlik, g.ham, g.indirimler); export const esleyiciIki = (g) => kur(g.id, g.agirlik, g.ucret.taban, g.ucret.indirimler);
// dto/istemciler.mjs — uc istemci yalniz veri aktarim nesnesinin alanlarini okur export const liste = (d) => `${d.liste.kimlik} ${d.liste.agirlik} kg ${d.liste.net}`; export const fatura = (d) => `${d.fatura.kimlik}: taban ${d.fatura.taban}, ${d.fatura.kalemler.map((k) => `${k.ad} ${k.tutar}`).join(", ")} = ${d.fatura.toplam}`; export const kutuk = (d) => `${d.kutuk.kimlik} net=${d.kutuk.net} indirim=${d.kutuk.indirimSayisi}`;
Sözleşme adlarının iç adlardan ayrıldığına dikkat etmek gerekir: içeride id, dışarıda
kimlik. Ayrım kasıtlıdır; iki adın aynı olması bir kolaylık gibi görünüp iki tarafı yeniden
birbirine bağlar.
Değişim Yayılımının Sayılması
Ölçüm iç modeli birinci sürümden ikinci sürüme geçirir ve iki düzende ne olduğunu sayar. Bir
çıktı undefined ya da NaN içeriyorsa, ya da hata atıyorsa bozulmuş sayılır.
// yayilim-say.mjs — ic model degistiginde bozulan istemci sayisi ve istemcideki ic alan adi import { readFileSync } from "node:fs"; import { surumBir, surumIki } from "./alan.mjs"; import * as dogrudan from "./dtosuz/istemciler.mjs"; import { esleyiciBir, esleyiciIki } from "./dto/esleyici.mjs"; import * as dto from "./dto/istemciler.mjs"; const ADLAR = ["liste", "fatura", "kutuk"]; const dene = (f, girdi) => { try { return f(girdi); } catch { return "HATA"; } }; const saglam = (s) => /undefined|NaN|HATA/.test(s) === false; function kosu(ad, birinci, ikinci) { const ilk = ADLAR.map((a) => dene(birinci[0][a], birinci[1])); const son = ADLAR.map((a) => dene(ikinci[0][a], ikinci[1])); const bozulan = son.filter((s) => saglam(s) === false).length; const degisen = son.filter((s, i) => s !== ilk[i]).length; console.log(`${ad}: bozulan istemci cikti = ${bozulan} / ${ADLAR.length}, degisen cikti = ${degisen} / ${ADLAR.length}`); return son; } console.log("ic model surum 1 -> surum 2"); kosu("dtosuz ", [dogrudan, surumBir()], [dogrudan, surumIki()]); const sonuc = kosu("dto ile ", [dto, esleyiciBir(surumBir())], [dto, esleyiciIki(surumIki())]); for (const s of sonuc) console.log(` ${s}`); const IC_ALAN = /\bg\.(ham|indirimler|notlar)\b|\bucret\.(taban|indirimler)\b/g; for (const dosya of ["dtosuz/istemciler.mjs", "dto/istemciler.mjs", "dto/esleyici.mjs"]) { const m = readFileSync(dosya, "utf8"); console.log(`${dosya.padEnd(24)} ic alan adi = ${[...m.matchAll(IC_ALAN)].length}`); } const dtoAlan = Object.values(esleyiciBir(surumBir())).reduce((t, d) => t + Object.keys(d).length, 0); const satir = readFileSync("dto/esleyici.mjs", "utf8").split("\n").filter((s) => s.trim() !== "").length; console.log(`dto alan sayisi = ${dtoAlan}, esleyici satir = ${satir}, alan basina satir = ${(satir / dtoAlan).toFixed(2)}`);
node yayilim-say.mjs
ic model surum 1 -> surum 2 dtosuz : bozulan istemci cikti = 3 / 3, degisen cikti = 3 / 3 dto ile : bozulan istemci cikti = 0 / 3, degisen cikti = 0 / 3 G7 3 kg 6480 G7: taban 8640, sozlesme -1296, hacim -864 = 6480 G7 net=6480 indirim=2 dtosuz/istemciler.mjs ic alan adi = 6 dto/istemciler.mjs ic alan adi = 0 dto/esleyici.mjs ic alan adi = 4 dto alan sayisi = 10, esleyici satir = 13, alan basina satir = 1.30
İç modelin biçimi değiştiğinde doğrudan okuyan üç istemcinin üçü de bozuldu. Eşleyici arkasındaki üç istemcinin hiçbiri bozulmadı ve üç çıktının üçü birinci sürümdekiyle birebir aynı kaldı — çıktı satırları bunu gösteriyor.
Sayının kaynağı sonraki üç satırda duruyor: istemci dosyası doğrudan okuyan düzende 6 iç alan adı taşıyor, eşleyicili düzende 0. İç yapı hakkındaki bilgi 4 adla tek dosyada, eşleyicinin içinde toplanmış durumda. Değişim yayılımı istemci sayısıyla büyüyen bir çarpımdan tek dosyalık sabite indi.
Bedelin Sayılması
Son satır bedeli veriyor. Üç sözleşme toplam 10 alan taşıyor ve eşleyici bunu 13 satırda üretiyor: alan başına 1,3 satır. Bu satırların işlevsel bir karşılığı yoktur; iç adı dış ada bağlamaktan başka iş yapmazlar. Yanına iki ek bedel gelir: 2 yeni dosya ve sınırdan her geçişte 1 ek çağrı.
İkinci bedel bakım tarafındadır. Sözleşmeye bir alan eklenmesi iki yerde iş çıkarır: eşleyici
o alanı üretmeli, istemci onu okumalı. Alan üretilmezse istemci undefined görür ve ölçümdeki
bozulma sayacı bunu yakalar. Bu nedenle eşleyicinin ürettiği kayıt bir sınamayla
kilitlenmelidir; sözleşme sınaması olmadan eşleyici sessizce eksik kalabilir.
Ne Zaman Uygulanmaz
Kalıbın kazancı iki sayının çarpımıdır: iç yapıya bağlı istemci sayısı ile iç yapının değişme sıklığı. İkisinden biri düşükse kazanç bedelin altına iner.
Tek istemcili bir sınırda iç modelin değişmesi zaten o tek istemciyi ilgilendirir; eşleyici onu 0 dosyadan korur ve karşılığında 13 satır ile 2 dosya ödetir. Aynı süreç içinde kalan ve aynı takımın baktığı çağrılarda da durum budur: alan nesnesini doğrudan geçirmek 0 satır tutar.
Ters yönde bir sınır daha var. Sözleşme iç modelin alan adlarını birebir kopyalıyorsa veri aktarım nesnesi bir yalıtım kurmaz, yalnız bir kopyalama katmanı ekler. Ölçüde bu durum şöyle görünür: iç model değiştiğinde eşleyici de değişir ve bozulma sayısı yine 3’e çıkar, çünkü sözleşme iç yapının aynası olmuştur. Kalıbın işe yaraması için sözleşmenin iç modelden bağımsız bir kararla tanımlanmış olması gerekir. Bu ayrımın adı Tasarım İlkeleri kursunda konulmuştu: politika ile ayrıntının ayrılması.
Özet
- Veri aktarım nesnesi dış sözleşmeyi ayrı bir düz kayıt olarak tanımlar; eşleyici iç modelden o kaydı üretir ve iç yapı bilgisini tek dosyada toplar.
- İç modelin biçimi değiştiğinde doğrudan okuyan 3 istemcinin 3’ü bozuldu; eşleyici arkasındaki 3 istemcinin 0’ı bozuldu ve üç çıktı birebir aynı kaldı.
- İstemci dosyası doğrudan okuyan düzende 6 iç alan adı taşıyordu, eşleyicili düzende 0; bilgi 4 adla eşleyicinin içine toplandı.
- Bedel sayıldı: 10 sözleşme alanı için 13 eşleyici satırı (alan başına 1,3 satır), 2 yeni dosya ve sınırdan her geçişte 1 ek çağrı.
- Kazanç istemci sayısı ile iç modelin değişme sıklığının çarpımıdır; tek istemcide ya da sözleşme iç adları birebir kopyaladığında kalıp bir kopyalama katmanına dönüşür.
Sonraki Adım
Bu dersin istemcileri kitaplığa hangi kapıdan girdiklerini hiç sormadı; eşleyicinin ürettiği kaydı hazır aldılar. Kaydı kimin ürettiği, senaryonun hangi sırayla yürüdüğü ve iş biriminin nerede açılıp kapandığı hâlâ belirsiz. Dört istemci aynı senaryoyu kendi içinde tekrar ederse adımlar dört kez yazılır. Sonraki ders uygulama sınırını servis katmanıyla tanımlar, sunum tarafının bilmek zorunda olduğu tip sayısını ve erişim zinciri derinliğini ölçer, ikinci bir istemci eklendiğinde tekrar eden senaryo adımı sayısını sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.