İçeriğe geç
academia.sh

Ders 14 / 18

Servis Yönelimli Mimari

İki bağlamın kurum çapında paylaşılan tek bir kayıt biçimi ve tek bir çağrı yolu üzerinden konuşması: kanonik kaydın alan sayısının, her servisin taşıdığı ama okumadığı alan sayısının, dönüştürme noktasının ve sınırdan geçen baytın ölçülmesi, dar sözleşmeyle karşılaştırılması ve ortak biçimde bir alan değiştiğinde düzenlenen dosya sayısının sayılması.

İçindekiler

Tek parça düzende iki bağlam aynı dağıtım biriminde durduğu için bir dosyalık değişiklik dört dosyayı yeniden yayınlatıyordu. İlk çözüm denemesi bağlamları birbirinden koparmak değil, aralarına paylaşılan bir tümleştirme yüzeyi koymaktır. Her bağlam kendi dağıtım birimine çıkar, ama ikisi de ortak bir kayıt biçimi ve ortak bir çağrı yolu üzerinden konuşur.

Servis yönelimli mimari (service-oriented architecture) bu düzeni kurumun tamamı için kurar: her yetenek kaba taneli bir servis olarak yayınlanır, servisler kendi gerçekleştirimlerini gizler ve aralarındaki alışveriş kurum çapında geçerli tek bir kayıt biçimi üzerinden yapılır. Buradaki servis, Tasarım Kalıpları kursundaki Servis Katmanı dersinde ölçülen uygulama içi katmanla aynı sözcüğü paylaşır ama farklı bir şeydir: orada servis bir uygulamanın iç sınırıydı, burada kendi başına yayınlanan bir dağıtım birimidir.

Kanonik Kayıt ve Ortak Çağrı Yolu

Biçemin merkezinde kanonik veri modeli (canonical data model) durur: iki bağlamın da kabul ettiği tek bir gönderi kaydı. Kayıt her iki bağlamın ihtiyacını karşılamak zorunda olduğu için alanları ikisinin birleşimidir.

// ortak/kanonik.mjs — kurum capinda tek gonderi kaydi: her servis ayni bicimi konusur
export const KANONIK_ALAN = [
  "id", "agirlik", "hacim", "bolge", "deger",
  "sozlesmeNo", "sozlesmeOrani", "tasiyiciKodu", "teslimDurumu", "sonZamanDamgasi",
];

export function kanonikYap(g) {
  const k = {};
  for (const a of KANONIK_ALAN) k[a] = g[a] ?? null;
  return k;
}

Çağrılar doğrudan servisten servise gitmez, paylaşılan bir yüzeyden geçer. Bu yüzey servisi adıyla bulur, gövdeyi kanonik biçimde taşır ve geçişleri sayar. Serileştirme burada gerçek: gövde metne çevrilir, karşı tarafta yeniden okunur.

// veriyolu/veriyolu.mjs — paylasilan tumlestirme yuzeyi: cagriyi kanonik bicimde tasir ve sayar
import { kanonikYap } from "../ortak/kanonik.mjs";

export function veriyolu(kayit) {
  const servisler = new Map();
  return {
    tanit(ad, servis) { servisler.set(ad, servis); },
    cagir(ad, islem, gonderi) {
      const govde = JSON.stringify(kanonikYap(gonderi));
      kayit.gecis.push(`veriyolu -> ${ad}.${islem}`);
      kayit.donusum += 1;
      kayit.bayt += govde.length;
      const sonuc = servisler.get(ad)[islem](JSON.parse(govde));
      const yanit = JSON.stringify(sonuc);
      kayit.gecis.push(`${ad}.${islem} -> veriyolu`);
      kayit.donusum += 1;
      kayit.bayt += yanit.length;
      return JSON.parse(yanit);
    },
  };
}

Her servis kanonik kaydı alır, kendi iç biçimine çevirir, işini yapar ve sonucu döndürür. OKUNAN_ALAN listesi servisin kanonik kayıttan gerçekten kullandığı alanları bildirir; ölçüm bu listeyi kullanır.

// servis/ucret-servisi.mjs — kaba taneli servis: kanonik kaydi alir, kendi bicimine cevirir
const TARIFE = { kademe: [[1, 3000], [5, 4800], [20, 9600]], bolge: { "34": 100, "06": 115, "35": 125 }, asgari: 2500 };
export const OKUNAN_ALAN = ["id", "agirlik", "bolge", "sozlesmeOrani"];

export const ucretServisi = {
  fiyatla(kanonik) {
    const g = { agirlik: kanonik.agirlik, bolge: kanonik.bolge, oran: kanonik.sozlesmeOrani ?? 0 };
    const kademe = TARIFE.kademe.find(([k]) => g.agirlik <= k) ?? [0, 9600];
    const taban = Math.max(TARIFE.asgari, Math.round((kademe[1] * TARIFE.bolge[g.bolge]) / 100));
    return { id: kanonik.id, net: taban - Math.round(taban * Math.min(g.oran, 0.4)) };
  },
};
// servis/operasyon-servisi.mjs — kaba taneli servis: ayni kanonik kayittan rota uretir
const AGAC = { "34": ["34"], "06": ["34", "06"], "35": ["34", "41", "35"] };
export const OKUNAN_ALAN = ["id", "bolge", "teslimDurumu"];

export const operasyonServisi = {
  planla(kanonik) {
    const rota = AGAC[kanonik.bolge] ?? ["34"];
    return {
      id: kanonik.id, rota, gun: rota.length,
      tasiyici: rota.length > 2 ? "MT" : "AN",
      durum: kanonik.teslimDurumu ?? "planlandi",
    };
  },
};

Paylaşılan Biçimin Ölçülmesi

Ölçüm dört soru sorar: bir istek kaç sınırdan geçiyor ve kaç dönüştürme noktası taşıyor, her servis kanonik kaydın kaç alanını gerçekten okuyor, aynı istek servise özel dar bir sözleşmeyle kaç bayta iner, ortak biçimde bir alan değiştiğinde kaç dosya düzenlenir.

// soa/olc.mjs — kanonik bicimin alan kullanimi, donusum noktasi, bayt ve ortak alan degisiminin etkisi
import { readFileSync } from "node:fs";
import { KANONIK_ALAN, kanonikYap } from "../ortak/kanonik.mjs";
import { veriyolu } from "../veriyolu/veriyolu.mjs";
import { ucretServisi, OKUNAN_ALAN as UCRET_ALAN } from "../servis/ucret-servisi.mjs";
import { operasyonServisi, OKUNAN_ALAN as OPERASYON_ALAN } from "../servis/operasyon-servisi.mjs";

const GONDERI = { id: "G1", agirlik: 4, hacim: 18, bolge: "35", deger: 12000, sozlesmeNo: "S7", sozlesmeOrani: 0.15 };
const kayit = { gecis: [], donusum: 0, bayt: 0 };
const yol = veriyolu(kayit);
yol.tanit("ucret", ucretServisi);
yol.tanit("operasyon", operasyonServisi);

const fiyat = yol.cagir("ucret", "fiyatla", GONDERI);
const plan = yol.cagir("operasyon", "planla", GONDERI);
console.log(`teklif = ${JSON.stringify({ id: fiyat.id, net: fiyat.net, gun: plan.gun, tasiyici: plan.tasiyici })}`);
console.log(`sinir gecisi = ${kayit.gecis.length}, donusum noktasi = ${kayit.donusum}, sinirdan gecen bayt = ${kayit.bayt}`);

console.log(`kanonik alan = ${KANONIK_ALAN.length}`);
for (const [ad, okunan] of [["ucret", UCRET_ALAN], ["operasyon", OPERASYON_ALAN]]) {
  console.log(`  ${ad.padEnd(9)} okudugu alan = ${okunan.length}, tasidigi ama okumadigi alan = ${KANONIK_ALAN.length - okunan.length}`);
}

const dar = (alanlar) => JSON.stringify(Object.fromEntries(alanlar.map((a) => [a, GONDERI[a] ?? null])));
const kanonikBayt = 2 * JSON.stringify(kanonikYap(GONDERI)).length;
const darBayt = dar(UCRET_ALAN).length + dar(OPERASYON_ALAN).length;
console.log(`iki istek govdesi: kanonik = ${kanonikBayt} bayt, servise ozel dar sozlesme = ${darBayt} bayt`);
for (const n of [2, 4, 8]) {
  console.log(`  servis = ${n}: kanonik donusturucu = ${n}, noktadan noktaya donusturucu = ${n * (n - 1)}`);
}

const DOSYA = ["ortak/kanonik.mjs", "veriyolu/veriyolu.mjs", "servis/ucret-servisi.mjs", "servis/operasyon-servisi.mjs"];
for (const alan of ["bolge", "sozlesmeOrani"]) {
  const gecen = DOSYA.filter((d) => readFileSync(d, "utf8").includes(alan));
  console.log(`"${alan}" alani degisirse duzenlenen dosya = ${gecen.length} (${gecen.join(", ")})`);
}
node soa/olc.mjs
teklif = {"id":"G1","net":5100,"gun":3,"tasiyici":"MT"}
sinir gecisi = 4, donusum noktasi = 4, sinirdan gecen bayt = 427
kanonik alan = 10
  ucret     okudugu alan = 4, tasidigi ama okumadigi alan = 6
  operasyon okudugu alan = 3, tasidigi ama okumadigi alan = 7
iki istek govdesi: kanonik = 326 bayt, servise ozel dar sozlesme = 101 bayt
  servis = 2: kanonik donusturucu = 2, noktadan noktaya donusturucu = 2
  servis = 4: kanonik donusturucu = 4, noktadan noktaya donusturucu = 12
  servis = 8: kanonik donusturucu = 8, noktadan noktaya donusturucu = 56
"bolge" alani degisirse duzenlenen dosya = 3 (ortak/kanonik.mjs, servis/ucret-servisi.mjs, servis/operasyon-servisi.mjs)
"sozlesmeOrani" alani degisirse duzenlenen dosya = 2 (ortak/kanonik.mjs, servis/ucret-servisi.mjs)

Sayıların Okunması

Tek parça derste teklif isteği 4 sınırdan geçiyordu, dönüştürme noktası 0, sınırdan geçen bayt 0’dı. Aynı çıktıyı üreten servis yönelimli düzende sınır geçişi yine 4 ama dönüştürme noktası 4’e, sınırdan geçen bayt 427’ye çıktı. Bu, biçemin ilk faturasıdır: her sınır artık bir serileştirme noktasıdır ve gövdenin biçimi bir sözleşmedir.

İkinci sayı kümesi kanonik modelin karakteristik maliyetini gösterir. Kayıt 10 alan taşıyor; ücret servisi bunların 4’ünü okuyor, 6’sını taşıyıp bırakıyor. Operasyon servisi 3’ünü okuyor, 7’sini taşıyıp bırakıyor. Bu boş alanlar yalnız bayt değildir: her biri servisin bilmek zorunda olmadığı bir kavramı sözleşmesine sokar. deger alanının anlamı değiştiğinde operasyon servisi o alanı hiç okumasa da sözleşmesi değişmiş olur.

Dar sözleşme karşılaştırması bedeli sayıya çeviriyor: aynı iki istek servise özel biçimlerle 101 bayt, kanonik biçimle 326 bayt. Kanonik modelin karşılığını aldığı yer ise dönüştürücü sayısıdır. İki servis varken iki düzen eşit; sekiz servise çıkıldığında kanonik düzende 8 dönüştürücü, noktadan noktaya düzende 56 dönüştürücü gerekir. Kanonik modelin gerekçesi budur ve servis sayısı arttıkça güçlenir.

Son iki satır biçemin gerçek sınırını gösterir. Ortak biçimde bolge alanı değişirse 3 dosya düzenlenir ve bunlardan ikisi ayrı dağıtım birimidir: iki servis birlikte yayınlanmak zorunda kalır. sozlesmeOrani değiştiğinde 2 dosya düzenlenir ve yalnız bir servis etkilenir. Yayın bağlaşımı tek parça düzende olduğu gibi %100 değil, ama sıfır da değil: paylaşılan sözleşmenin kapsadığı her alan bir bağlaşım noktasıdır.

Biçemin Yerleştiği Yer

Sayılar bir kalite niteliği ödünleşimini betimliyor. Kazanılan nitelik uyumluluk ve yeniden kullanımdır: on alanlık tek bir kayıt biçimi öğrenildiğinde kuruma yeni katılan bir servis mevcut iki servisle konuşmak için 1 dönüştürücü yazar, iki ayrı biçim öğrenmek zorunda kalmaz. Bırakılan nitelik bağımsız değişebilirliktir: paylaşılan biçimin her alanı, o alanı okumayan servisleri de yayına bağlar.

Bu yüzden biçemin uygulandığı yer kurum ölçeğidir. İki servis için kanonik model kazanç getirmez — dönüştürücü sayısı iki düzende de 2, karşılığında istek gövdesi üç katına çıkar. Servis sayısı arttıkça ve servisler ayrı ekiplere dağıldıkça denge tersine döner: öğrenilecek biçim sayısı 1’de kalır, dönüştürücü sayısı doğrusal büyür.

Özet

  • Servis yönelimli mimari her yeteneği kaba taneli bir dağıtım birimi olarak yayınlar ve alışverişi kurum çapında tek bir kanonik kayıt biçimi üzerinden yürütür.
  • Aynı teklif isteği tek parça düzende 0 dönüştürme noktası ve 0 bayt taşırken bu düzende 4 dönüştürme noktası ve 427 bayt taşıdı.
  • On alanlık kanonik kaydın ücret servisi 4, operasyon servisi 3 alanını okudu; taşınıp okunmayan alan sayısı 6 ve 7 oldu.
  • İki istek dar sözleşmelerle 101 bayt, kanonik biçimle 326 bayt tuttu; kanonik modelin karşılığı dönüştürücü sayısıdır: sekiz servis için 8’e karşı 56.
  • Ortak biçimde bolge alanı değiştiğinde 3 dosya düzenlendi ve iki servis birlikte yayınlanmak zorunda kaldı; yayın bağlaşımı %100’den düştü ama sıfırlanmadı.
  • Biçem kurum ölçeğinde kazanır: öğrenilecek biçim sayısı 1’de kalırken dönüştürücü sayısı servis sayısıyla doğrusal büyür.

Sonraki Adım

Paylaşılan sözleşme bağlaşımın kaynağı olduğu ortaya çıktı: on alanın hepsi, o alanı okumayan servisi bile yayına bağlıyor. Bir sonraki adım ortak biçimi kaldırmaktır. Her servis kendi sözleşmesini, kendi veri biçimini ve kendi yayın takvimini alır; aralarındaki çağrı ortak bir yüzeyden değil doğrudan geçer. Bu düzen bağımsız yayını gerçekten sağlar ama karşılığında yeni bir sayı üretir: bir servis durduğunda kaç isteğin yanıtlanmaya devam ettiği. Sonraki ders iki servisi ayrı yerel süreç olarak çalıştırır, sınır geçişini ve gövde boyutunu ölçer, sonra bir süreci durdurup ayakta kalan uç 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