İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat