Ders 03 / 18
Servis Yönelimli Mimari
Kurumsal servis düşüncesinin ölçüsü: birimlerin ortak bir sözleşme ve kanonik veri modeli üzerinden konuşması, ortak katmana bağlı birim sayısının sayılması, kanonik bir alan adının değişmesinin kaç birimi dokunulur kıldığı ve sahibi güncellenip tüketicisi güncellenmediğinde doğan sessiz yanlış davranış.
İçindekiler
Önceki ders sınırı ithal grafiğinde kurdu, ama depo.mjs bütün alanların ortak veri yapısını
tutmayı sürdürdü. Sınır kodda vardı, veride yoktu. Bu ders o ortaklığı büyütür: alanlar
kendi durumlarını taşıyan ayrı birimlere dönüşür, aralarında ortak bir yol üzerinden konuşur
ve konuşmanın biçimini kurumsal ortak bir model belirler.
Bu düzenin adı servis yönelimli mimari (service-oriented architecture). Biçemin tanımı Mimari Biçemler kursunda yapıldı; burada ölçülen şey tanım değil, ortak katmanın koda getirdiği bağlaşımdır.
MS4. Birimler ayrı süreç değildir; hepsi aynı süreçte, bir ileti yolu üzerinden konuşur. Ölçülen şey sözleşme bağlaşımıdır, ağ değil. Süreçlere bölme sonraki derste yapılır.
Ortak Katman
Ortak katman iki şey tanımlar: her iletinin taşıyacağı zarf ve kanonik üye kaydının alanları.
// ortak/sozlesme.mjs — kurumsal ortak model: ileti zarfi ve kanonik uye alanlari export const ZARF = ["kaynak", "islem", "govde"]; export const UYE_ALANLARI = ["uyeNo", "ad", "acikOdunc", "sinir", "borc"]; export function dogrula(ileti) { for (const a of ZARF) if (a in ileti === false) throw new Error(`zarf alani eksik: ${a}`); return ileti; }
// yol.mjs — kurumsal servis yolu; her ileti ortak sozlesmeye gore dogrulanir import { dogrula } from "./ortak/sozlesme.mjs"; const birimler = new Map(); export const sayac = { ileti: 0 }; export function tanit(islem, islev) { birimler.set(islem, islev); } export function gonder(ileti) { sayac.ileti += 1; return birimler.get(dogrula(ileti).islem)(ileti.govde); }
Her birim kendi durumunu taşır ve yaptığı işi yola tanıtır. Doğrudan ithal yoktur; birimler birbirinin adını değil, işlem adını bilir.
// katalog.mjs — kitap birimi import { tanit } from "./yol.mjs"; const kitaplar = { 1: { kitapNo: 1, baslik: "Korluk", durum: "rafta" }, 2: { kitapNo: 2, baslik: "Kum Kitabi", durum: "rafta" } }; tanit("kitap.durum", ({ kitapNo }) => kitaplar[kitapNo].durum); tanit("kitap.oduncIsaretle", ({ kitapNo }) => { kitaplar[kitapNo].durum = "odunc"; });
// uyelik.mjs — kanonik uye kaydini tutan birim import { tanit } from "./yol.mjs"; import { UYE_ALANLARI } from "./ortak/sozlesme.mjs"; const uyeler = { 4: { uyeNo: 4, ad: "Emre", acikOdunc: 1, sinir: 3, borc: 0 }, 5: { uyeNo: 5, ad: "Selin", acikOdunc: 3, sinir: 3, borc: 0 }, }; export const eksikAlan = (u) => UYE_ALANLARI.filter((a) => a in u === false); tanit("uye.getir", ({ uyeNo }) => uyeler[uyeNo]); tanit("uye.oduncEkle", ({ uyeNo }) => { uyeler[uyeNo].acikOdunc += 1; }); tanit("uye.borcEkle", ({ uyeNo, tutar }) => { uyeler[uyeNo].borc += tutar; });
// ucretlendirme.mjs — gecikme ucreti birimi import { tanit } from "./yol.mjs"; tanit("ucret.hesapla", ({ gecikmeGun }) => (gecikmeGun > 0 ? gecikmeGun * 2 : 0));
// bildirim.mjs — bildirim birimi; kanonik kayittan ad alanini okur import { tanit, gonder } from "./yol.mjs"; tanit("bildirim.gonder", ({ uyeNo, metin }) => { const uye = gonder({ kaynak: "bildirim", islem: "uye.getir", govde: { uyeNo } }); console.log(` bildirim -> ${uye.ad}: ${metin}`); });
Ödünç birimi iş akışını yürütür. Dikkat edilecek yer sınır denetimidir: kanonik kaydı alıyor ve kuralı kendisi uyguluyor.
// odunc.mjs — is akisi; kanonik uye kaydini okuyup kurali kendisi uygular import { gonder, sayac } from "./yol.mjs"; export function oduncVer(kitapNo, uyeNo, gecikmeGun = 0) { const z = (islem, govde) => gonder({ kaynak: "odunc", islem, govde }); const uye = z("uye.getir", { uyeNo }); if (uye.acikOdunc >= uye.sinir) throw new Error("odunc siniri asildi"); if (z("kitap.durum", { kitapNo }) !== "rafta") throw new Error("kitap rafta degil"); z("kitap.oduncIsaretle", { kitapNo }); z("uye.oduncEkle", { uyeNo }); const tutar = z("ucret.hesapla", { gecikmeGun }); if (tutar > 0) z("uye.borcEkle", { uyeNo, tutar }); z("bildirim.gonder", { uyeNo, metin: `kitap ${kitapNo} odunc verildi` }); return { tutar, ileti: sayac.ileti }; }
// uygulama.mjs — butun birimleri yukler ve is akisini kosturur import "./katalog.mjs"; import "./uyelik.mjs"; import "./ucretlendirme.mjs"; import "./bildirim.mjs"; import { oduncVer } from "./odunc.mjs"; for (const [kitapNo, uyeNo, gun] of [[1, 4, 3], [2, 5, 0]]) { try { const s = oduncVer(kitapNo, uyeNo, gun); console.log(`odunc verildi: kitap ${kitapNo} -> uye ${uyeNo}, ucret ${s.tutar}, ileti ${s.ileti}`); } catch (h) { console.log(`basarisiz (uye ${uyeNo}):`, h.message); } }
node uygulama.mjs
bildirim -> Emre: kitap 1 odunc verildi odunc verildi: kitap 1 -> uye 4, ucret 6, ileti 8 basarisiz (uye 5): odunc siniri asildi
Birinci ölçü ileti sayısıdır: bir ödünç iş akışı 8 ileti üretti. Birinci derste aynı iş
akışı 6 yerel çağrıyla yürüyordu. Fark iki yerden geliyor. Bildirim birimi artık üyenin adını
almak için ayrı bir ileti gönderiyor; ödünç birimi de uygunMu gibi bir davranış çağırmak
yerine kanonik kaydı çekip kuralı kendisi uyguluyor. Ortak model konuşmayı davranıştan
veriye kaydırdı.
Ortak Modele Bağlı Birim
Bağlaşımın ölçüsü, kanonik alan adlarına kaç birimden atıf yapıldığıdır.
// bagalasim.mjs — ortak modeldeki alan adlarina hangi birimden atif var import { readdirSync, readFileSync } from "node:fs"; import { UYE_ALANLARI } from "./ortak/sozlesme.mjs"; const disarida = ["bagalasim.mjs", "uygulama.mjs", "yol.mjs"]; const birimler = readdirSync(".").filter((a) => a.endsWith(".mjs") && disarida.includes(a) === false); let bagli = 0; for (const b of birimler.sort()) { const metin = readFileSync(b, "utf8"); const bulunan = UYE_ALANLARI.filter((a) => new RegExp(`\\b${a}\\b`).test(metin)); if (bulunan.length > 0) bagli += 1; console.log(`${b.padEnd(20)} kanonik alan atfi: ${bulunan.join(", ") || "(yok)"}`); } console.log(`birim: ${birimler.length} ortak modele bagli birim: ${bagli}`);
node bagalasim.mjs
bildirim.mjs kanonik alan atfi: uyeNo, ad katalog.mjs kanonik alan atfi: (yok) odunc.mjs kanonik alan atfi: uyeNo, acikOdunc, sinir ucretlendirme.mjs kanonik alan atfi: (yok) uyelik.mjs kanonik alan atfi: uyeNo, ad, acikOdunc, sinir, borc birim: 5 ortak modele bagli birim: 3
Beş birimin üçü kanonik üye kaydının alan adlarını biliyor. Önceki derste bu sayı birdi:
acik alanını yalnız üyelik alanı tanıyordu, ödünç ona uygunMu diye soruyordu. Ortak
model, kaydın iç yapısını üç birime yaydı.
Buradaki ayrım şudur: bildirim.mjs ile odunc.mjs üye kaydına sahip değil, ama onun
biçimine bağlı. Veri sahipliği üyelik biriminde, veri bilgisi üç yerde.
Ortak Katmanın Değişmesi
Kanonik modelin bir alanı yeniden adlandırılsın. Değişikliği ortak katmanda ve alanın sahibinde yapmak yeterli görünür.
# degistir.sh — kanonik alan adi degisir; yalniz ortak katman ve alanin sahibi guncellenir sed -i.y 's/acikOdunc/acikOduncSayisi/g' ortak/sozlesme.mjs uyelik.mjs rm -f ortak/sozlesme.mjs.y uyelik.mjs.y echo "dokunulan dosya: 2" node uygulama.mjs
dokunulan dosya: 2 bildirim -> Emre: kitap 1 odunc verildi odunc verildi: kitap 1 -> uye 4, ucret 6, ileti 8 bildirim -> Selin: kitap 2 odunc verildi odunc verildi: kitap 2 -> uye 5, ucret 0, ileti 15
İkinci istek geçti. Üye 5 sınırındaydı ve bir önceki koşumda reddedilmişti; şimdi ödünç aldı. Hiçbir hata basılmadı, hiçbir yerde bir uyarı çıkmadı.
Sebep ödünç birimindeki tek satırdır: uye.acikOdunc artık undefined döndürüyor ve
undefined >= 3 karşılaştırması false veriyor, yani sınır denetimi her zaman geçiyor.
Ortak modeldeki bir ad değişikliği, tüketicide hata değil yanlış davranış üretti.
Düzeltme, aynı değişikliğin tüketici birime de taşınmasıdır.
# duzelt.sh — ayni degisiklik tuketici birime de tasinir sed -i.y 's/acikOdunc/acikOduncSayisi/g' odunc.mjs rm -f odunc.mjs.y echo "dokunulan dosya: 3 (ortak/sozlesme.mjs, uyelik.mjs, odunc.mjs)" node uygulama.mjs
dokunulan dosya: 3 (ortak/sozlesme.mjs, uyelik.mjs, odunc.mjs) bildirim -> Emre: kitap 1 odunc verildi odunc verildi: kitap 1 -> uye 4, ucret 6, ileti 8 basarisiz (uye 5): odunc siniri asildi
Ölçü şudur: ortak katmandaki bir alan adı değişikliği 3 dosyaya dokundu ve bu üç dosya
birlikte yayına çıkmak zorunda. Birimler ayrı ayrı tanıtılmış olsa da, kanonik model onları
tek bir sürüm adımına bağlıyor. Yerinde düzenlemede -i seçeneği GNU ve BSD sürümlerinde
farklı davrandığı için yedek uzantısı verilip yedek silinmiştir.
Üç Sütun
| Servis yönelimli mimari | |
|---|---|
| Ucuzlattığı | Birimler birbirini ithal etmiyor; yeni bir birim eklemek yalnız bir işlem adı tanıtmakla oluyor, mevcut birimlerin hiçbiri değişmiyor |
| Pahalılaştırdığı | Ortak modele bağlı birim 1’den 3’e çıktı; kanonik bir alan adı değişikliği 3 dosyayı birlikte yayına zorluyor; iş akışı 6 çağrı yerine 8 ileti üretiyor |
| Doğan arıza kipi | Ortak modelin sahibi güncellenip tüketicisi güncellenmediğinde hata alınmıyor; sınır denetimi sessizce devre dışı kalıyor |
Üçüncü sütun bu dersin asıl kazancıdır. Ortak katman bir bağlaşım noktasıdır ve o noktadan gelen kusurlar çoğunlukla çökme değil, sessiz yanlış davranış biçiminde görünür. Ödünç birimi çalıştı, cevap döndürdü, kayıt yazdı; yalnız uygulamadığı bir kural vardı.
Özet
- Servis yönelimli düzende birimler birbirini ithal etmiyor, ortak bir yol üzerinden işlem adıyla konuşuyor; yeni birim eklemek mevcut birimleri değiştirmiyor.
- Ortak kanonik model konuşmayı davranıştan veriye kaydırdı: iş akışı 6 yerel çağrı yerine 8 ileti üretti, çünkü tüketiciler kaydı çekip kuralı kendileri uyguluyor.
- Kanonik alan adlarını bilen birim sayısı 1’den 3’e çıktı; veri sahipliği tek birimde kalırken veri bilgisi üçe yayıldı.
- Ortak katmandaki bir alan adı değişikliği 3 dosyaya dokundu ve üçünü tek bir sürüm adımına bağladı.
- Sahibi güncellenip tüketicisi güncellenmediğinde sistem hata vermedi; sınırındaki üyeye ödünç verildi. Ortak model kusurları sessiz yanlış davranış olarak üretiyor.
Sonraki Adım
Bu düzende birimler ayrıldı, ama ayrılmanın bedeli hâlâ ödenmedi: hepsi aynı süreçte
koşuyor, gonder bir işlev çağrısı, ileti bir nesne. Bir birim çökerse hepsi çöker, bir
birim yeniden yayına çıkarsa hepsi çıkar. Sonraki ders aynı iş akışını gerçekten ayrı
süreçlere böler: her birim kendi bağlantı noktasında dinler, iletiler ağ üzerinden gider.
Ölçülecek şeyler süreç sayısı, uç sayısı, istek başına ağ atlaması, zincir gecikmesi, bir
değişikliğin dokunduğu dağıtım birimi ve sistemi ayağa kaldırma adımıdır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.