İçeriğe geç
academia.sh

Ders 01 / 11

Yazılım Mimarisi Tanımı

Yazılım mimarisinin geri alınması pahalı kararlar kümesi olarak tanımlanması: model bir kod tabanında her kararın geri alınma maliyetinin bağımlı kapanıştan okunması, kararların bu sayıya göre sıralanması, eşiğin kaynağının yazılması ve gövde büyüklüğüne göre sıralamayla karşılaştırılması.

İçindekiler

Önkoşul müfredatlar kararların kendisini kurdu. Yazılım Tasarımı müfredatı bağlaşımı, bütünlüğü, tasarım kalıplarını ve mimari biçemleri tanımladı ve her birini ölçtü; Sistem Tasarımı müfredatı ölçekleme, dayanıklılık ve veri dağıtımı kararlarını varsayımdan hesaba, hesaptan ölçüme bağladı. İki müfredat da şu soruya yanıt verdi: bu seçenekler arasındaki fark nedir ve hangi sayıyla görünür.

Hiçbiri şunu sormadı: bu kararı kim, neye göre, hangi alternatifi eleyerek verir ve karar verildikten sonra ona ne olur. Bu müfredatın konusu kararların kendisi değil, kararın süreci ve izidir. İlk soru tanımdır: bir kod tabanındaki yüzlerce karardan hangileri “mimari” sayılır. Bu ders o ayrımı bir sayıya bağlar — kararın geri alınma maliyetine.

Kararın Gövdesi ve Geri Alınması

Bir karar kodda bir yerde durur. “Ödünç süresi kuralı yapılandırmadan okunur” kararının gövdesi kural modülüdür; “kimlik doğrulama tek modülde toplanır” kararının gövdesi o modüldür. Gövde küçük olabilir. Kararı geri almanın maliyeti ise gövdeyle değil, gövdeye bağımlı olan her şeyle ölçülür: kararı bozmak için gövdeyi değiştirmek yetmez, gövdeye doğrudan ya da dolaylı bağımlı her modüle dokunmak gerekir.

Ölçüm için bir kod tabanı gerekir. Aşağıdaki dosya, bu müfredat boyunca sürecek örneğin modelidir: bölgesel bir kütüphane ağının yazılım sistemi. Ağda birden çok şube, kendi bütçesi olan bir bilgi işlem birimi, dışarıdan alınmış bir katalog sistemi ve içeride yazılmış ödünç servisleri vardır. Bu bir modeldir; gerçek bir kurum anlatılmıyor. Modül adları, dosya sayıları ve ithal kenarları elle seçilmiştir (MN1).

// mimari/kod-tabani.mjs — bolgesel kutuphane agi yazilim sisteminin MODEL kod tabani.
// Gercek bir kurum degildir; modul adlari, dosya sayilari ve ithal kenarlari elle secilmistir.
export const DOSYA = {
  yapilandirma: 3, kayit: 4, zaman: 2, "veri-erisim": 9, "olay-yolu": 7,
  "kimlik-dogrulama": 8, yetki: 5, "katalog-baglayici": 11, "katalog-onbellek": 6,
  "odunc-kural": 5, "odunc-cekirdek": 14, ayirtma: 8, "uye-kayit": 9, "uye-ceza": 6,
  "sube-envanter": 10, "sube-esitleme": 12, "bildirim-kuyruk": 6, "bildirim-eposta": 15,
  "rapor-gunluk": 7, "rapor-toplu": 13, "web-ucu": 18, "personel-ucu": 22,
  "kiosk-ucu": 9, "toplu-is": 8,
};

// ITHAL[a] = a modulunun ithal ettigi moduller (a -> b: a, b'ye baglidir)
export const ITHAL = {
  yapilandirma: [], zaman: [], kayit: ["yapilandirma"],
  "veri-erisim": ["yapilandirma", "kayit"],
  "olay-yolu": ["yapilandirma", "kayit"],
  "kimlik-dogrulama": ["veri-erisim", "yapilandirma", "kayit"],
  yetki: ["kimlik-dogrulama"],
  "katalog-baglayici": ["yapilandirma", "kayit"],
  "katalog-onbellek": ["katalog-baglayici", "zaman"],
  "odunc-kural": ["yapilandirma", "zaman"],
  "odunc-cekirdek": ["odunc-kural", "veri-erisim", "olay-yolu", "zaman", "katalog-baglayici"],
  ayirtma: ["odunc-cekirdek", "bildirim-kuyruk", "veri-erisim"],
  "uye-kayit": ["veri-erisim", "kayit"],
  "uye-ceza": ["uye-kayit", "odunc-kural", "zaman", "veri-erisim"],
  "sube-envanter": ["veri-erisim", "olay-yolu"],
  "sube-esitleme": ["sube-envanter", "olay-yolu", "katalog-baglayici", "kayit"],
  "bildirim-kuyruk": ["veri-erisim", "olay-yolu"],
  "bildirim-eposta": ["bildirim-kuyruk", "yapilandirma"],
  "rapor-gunluk": ["veri-erisim", "zaman"],
  "rapor-toplu": ["veri-erisim", "sube-envanter", "zaman", "kayit"],
  "web-ucu": ["odunc-cekirdek", "katalog-onbellek", "uye-kayit", "ayirtma", "kimlik-dogrulama"],
  "personel-ucu": ["odunc-cekirdek", "uye-kayit", "uye-ceza", "sube-envanter", "rapor-gunluk",
    "kimlik-dogrulama", "yetki"],
  "kiosk-ucu": ["odunc-cekirdek", "katalog-onbellek", "kimlik-dogrulama"],
  "toplu-is": ["rapor-toplu", "sube-esitleme", "bildirim-kuyruk", "bildirim-eposta"],
};

// KARAR[i].govde = kararin kodda karsiligi olan moduller (kararin govdesi)
export const KARAR = [
  { ad: "veri erisimi tek katmandan gecer", govde: ["veri-erisim"] },
  { ad: "ayar degerleri tek kaynaktan okunur", govde: ["yapilandirma"] },
  { ad: "envanter olay yolu uzerinden esitlenir", govde: ["olay-yolu", "sube-esitleme"] },
  { ad: "dis kataloga tek baglayici uzerinden erisilir", govde: ["katalog-baglayici"] },
  { ad: "kimlik dogrulama tek modulde toplanir", govde: ["kimlik-dogrulama"] },
  { ad: "odunc suresi kurali yapilandirmadan okunur", govde: ["odunc-kural"] },
  { ad: "saat kaynagi tek modulden alinir", govde: ["zaman"] },
  { ad: "ceza hesabi uye kaydindan ayri durur", govde: ["uye-ceza"] },
  { ad: "gunluk rapor ayri bir modulde uretilir", govde: ["rapor-gunluk"] },
  { ad: "eposta bildirimi duz metin govdeyle gider", govde: ["bildirim-eposta"] },
  { ad: "kiosk ucu ayri bir dagitim birimidir", govde: ["kiosk-ucu"] },
];

export const MODULLER = Object.keys(DOSYA);

// b modulune dogrudan ya da dolayli olarak bagimli olan moduller (ters kapanis)
export function bagimliKapanis(cekirdek) {
  const kapali = new Set(cekirdek);
  let buyudu = true;
  while (buyudu) {
    buyudu = false;
    for (const m of MODULLER) {
      if (kapali.has(m)) continue;
      if (ITHAL[m].some((h) => kapali.has(h))) { kapali.add(m); buyudu = true; }
    }
  }
  return kapali;
}

export const dosyaTopla = (kume) => [...kume].reduce((t, m) => t + DOSYA[m], 0);

Ölçünün tanımı bagimliKapanis işlevindedir: bir modül kümesinden başlanır ve kümedeki bir modüle bağımlı olan her modül kümeye eklenir; küme büyümeyi durdurana kadar sürer. Bu, kararın erişim alanıdır. Geri alma maliyeti bu alandaki modül ve dosya sayısıdır (MN2). Sayı bir üst sınırdır: bağımlı her modüle gerçekten dokunulacağı varsayılıyor, bazılarında değişiklik gerekmeyebilir. Alt sınır değil üst sınır ölçmek, bu derste kasıtlıdır — kararın ne kadar pahalıya mal olabileceği sorulmaktadır.

Ölçüm

İkinci dosya on bir kararı sıralar ve iki eşik dener.

// mimari/geri-alma.mjs — her kararin geri alinma maliyeti ve iki siralamanin karsilastirmasi
import { DOSYA, KARAR, MODULLER, bagimliKapanis, dosyaTopla } from "./kod-tabani.mjs";

const toplamDosya = dosyaTopla(new Set(MODULLER));
console.log(`model kod tabani: ${MODULLER.length} modul, ${toplamDosya} dosya`);

const olcum = KARAR.map((k) => {
  const yerelDosya = k.govde.reduce((t, m) => t + DOSYA[m], 0);
  const kapali = bagimliKapanis(k.govde);
  return { ad: k.ad, yerelModul: k.govde.length, yerelDosya,
    yayilanModul: kapali.size, yayilanDosya: dosyaTopla(kapali) };
});
const sirali = [...olcum].sort((a, b) => b.yayilanDosya - a.yayilanDosya);

const sut = (s, n) => String(s).padEnd(n);
console.log("\n" + sut("karar", 46) + sut("govde", 10) + sut("geri alma", 12) + "kat");
console.log("-".repeat(74));
for (const o of sirali)
  console.log(sut(o.ad, 46) + sut(`${o.yerelModul}m/${o.yerelDosya}d`, 10) +
    sut(`${o.yayilanModul}m/${o.yayilanDosya}d`, 12) + (o.yayilanDosya / o.yerelDosya).toFixed(1) + "x");

// esik 1: acikca secilmis kural — kod tabaninin yarisindan cogu
const esikA = Math.floor(toplamDosya / 2) + 1;
const ustA = sirali.filter((o) => o.yayilanDosya >= esikA);
const ort = (d) => (d.reduce((t, o) => t + o.yayilanDosya, 0) / d.length).toFixed(1);
console.log(`\nesik A (secilmis kural, kod tabaninin yarisi) = ${esikA} dosya`);
console.log(`  ustunde ${ustA.length} karar (ort. ${ort(ustA)} dosya), altinda ${sirali.length - ustA.length} karar (ort. ${ort(sirali.slice(ustA.length))} dosya)`);
console.log(`  esige en yakin karar esigin ${esikA - sirali[ustA.length].yayilanDosya} dosya altinda`);

// esik 2: siralamadaki en buyuk mutlak bosluk
let enBosluk = 0, kesim = 0;
for (let i = 0; i < sirali.length - 1; i++) {
  const b = sirali[i].yayilanDosya - sirali[i + 1].yayilanDosya;
  if (b > enBosluk) { enBosluk = b; kesim = i + 1; }
}
console.log(`esik B (siralamadaki en buyuk bosluk) = ${sirali[kesim].yayilanDosya + 1} dosya (${sirali[kesim - 1].yayilanDosya} -> ${sirali[kesim].yayilanDosya}, ${enBosluk} dosya atlama)`);
console.log(`  ustunde ${kesim} karar, altinda ${sirali.length - kesim} karar`);
console.log(`iki esigin ortak sectigi karar = ${kesim} / ${ustA.length}; siralama ayni, sinir farkli`);

// karsilastirma: govde buyuklugune gore siralama / geri alma maliyetine gore siralama
const A = [...olcum].sort((a, b) => b.yerelDosya - a.yerelDosya).map((o) => o.ad);
const B = sirali.map((o) => o.ad);
let ters = 0;
for (let i = 0; i < A.length; i++)
  for (let j = i + 1; j < A.length; j++) if (B.indexOf(A[i]) > B.indexOf(A[j])) ters++;
const cift = (A.length * (A.length - 1)) / 2;
const ortak = A.slice(0, 4).filter((x) => B.slice(0, 4).includes(x)).length;
console.log(`\ngovde siralamasi <-> geri alma siralamasi: ${ters}/${cift} cift ters (${((ters / cift) * 100).toFixed(0)}%)`);
console.log(`ilk dort kararda ortusme = ${ortak}/4`);
const enA = olcum.find((o) => o.ad === A[0]), enB = olcum.find((o) => o.ad === B[0]);
console.log(`govdesi en buyuk karar : ${enA.yerelDosya} dosya govde, ${enA.yayilanDosya} dosya geri alma`);
console.log(`geri almasi en pahali  : ${enB.yerelDosya} dosya govde, ${enB.yayilanDosya} dosya geri alma`);
model kod tabani: 24 modul, 217 dosya

karar                                         govde     geri alma   kat
--------------------------------------------------------------------------
ayar degerleri tek kaynaktan okunur           1m/3d     23m/215d    71.7x
veri erisimi tek katmandan gecer              1m/9d     17m/179d    19.9x
envanter olay yolu uzerinden esitlenir        2m/19d    12m/142d    7.5x
saat kaynagi tek modulden alinir              1m/2d     12m/118d    59.0x
dis kataloga tek baglayici uzerinden erisilir 1m/11d    9m/108d     9.8x
odunc suresi kurali yapilandirmadan okunur    1m/5d     7m/82d      16.4x
kimlik dogrulama tek modulde toplanir         1m/8d     5m/62d      7.8x
gunluk rapor ayri bir modulde uretilir        1m/7d     2m/29d      4.1x
ceza hesabi uye kaydindan ayri durur          1m/6d     2m/28d      4.7x
eposta bildirimi duz metin govdeyle gider     1m/15d    2m/23d      1.5x
kiosk ucu ayri bir dagitim birimidir          1m/9d     1m/9d       1.0x

esik A (secilmis kural, kod tabaninin yarisi) = 109 dosya
  ustunde 4 karar (ort. 163.5 dosya), altinda 7 karar (ort. 48.7 dosya)
  esige en yakin karar esigin 1 dosya altinda
esik B (siralamadaki en buyuk bosluk) = 143 dosya (179 -> 142, 37 dosya atlama)
  ustunde 2 karar, altinda 9 karar
iki esigin ortak sectigi karar = 2 / 4; siralama ayni, sinir farkli

govde siralamasi <-> geri alma siralamasi: 31/55 cift ters (56%)
ilk dort kararda ortusme = 2/4
govdesi en buyuk karar : 19 dosya govde, 142 dosya geri alma
geri almasi en pahali  : 3 dosya govde, 215 dosya geri alma

Tablonun son sütunu ölçünün asıl bulgusudur. Ayar değerlerinin tek kaynaktan okunması kararı üç dosyalık bir gövdede durur; geri alınması 23 modüle ve 215 dosyaya, yani model kod tabanının neredeyse tamamına dokunur — 71,7 kat. Saat kaynağının tek modülden alınması iki dosyalık bir gövdeyle 118 dosyaya yayılır. Öteki uçta, kiosk ucunun ayrı bir dağıtım birimi olması kararı dokuz dosyada başlar ve dokuz dosyada biter: hiçbir modül ona bağımlı değildir, oran 1,0.

Eşiğin Kaynağı

Sıralama tek başına bir ayrım vermez; ayrım için bir eşik gerekir ve eşiğin bir kaynağı olmak zorundadır. Çıktıda iki eşik denendi. Eşik A açıkça seçilmiş bir kuraldır: geri alınması kod tabanının yarısından çoğuna dokunan karar (MN3). Bu kural 109 dosya sınırını verir ve dört kararı ayırır; üstteki dördün ortalaması 163,5, alttaki yedinin ortalaması 48,7 dosyadır — aradaki oran 3,4. Eşik B ölçülen dağılımdan gelir: sıralamadaki en büyük atlama 179’dan 142’ye düşen 37 dosyalık boşluktur ve iki kararı ayırır.

İki eşik farklı sayılar veriyor ve bu, tanımın kırılgan yeri. Eşik A’ya en yakın karar sınırın bir dosya altında kalıyor; bir dosyalık bir fark kararı sınıf değiştiriyor. Buradan çıkan kural şudur: sıralama savunulabilir, sınır tartışmaya açıktır. Bir karara “mimari açıdan önemli” denirken söylenmesi gereken şey sınıf değil, sıradaki yer ve o yeri veren sayıdır. “Bu karar mimaridir” cümlesi ölçüsüzdür; “bu kararı geri almak 23 modüle, 215 dosyaya dokunur” cümlesi denetlenebilir.

Gövdeye Bakmak Neden Yanıltır

Karşılaştırma iki sıralamayı aynı girdide koşturuyor. Birincisi kararı gövdesinin büyüklüğüne göre sıralıyor: değişiklik ne kadar yer kaplıyor. İkincisi geri alma maliyetine göre sıralıyor. Aynı on bir karar için iki sıralama 55 çiftin 31’inde ters düşüyor — %56. İlk dört sırada yalnız iki karar ortak.

Uçlar farkı açıkça gösteriyor. Gövdesi en büyük karar 19 dosyalık bir gövdeye sahip ve geri alınması 142 dosyaya dokunuyor; geri alınması en pahalı karar ise üç dosyalık bir gövdede duruyor ve 215 dosyaya dokunuyor. E-posta bildiriminin düz metin gövdeyle gitmesi kararı gövde büyüklüğünde 15 dosyayla üst sıralarda, geri alma maliyetinde 23 dosyayla sondan üçüncü. Bir gözden geçirmede “büyük değişiklik” diye işaretlenecek olan odur; geri alınamayacak olan ise üç satırlık ayar okuma kararıdır.

Bu, ölçünün yönünü belirler. Bir kararın ağırlığı yazılırken harcanan emekten değil, kararın başkalarınca ne kadar varsayıldığından okunur. Yazılım Tasarımı müfredatının bağlaşım ölçüsüyle aynı yöne bakılıyor; fark, oradaki ölçünün modüller arası bağı, buradakinin bir kararın erişim alanını sayması.

Tanım

Ölçü tanımı verir. Yazılım mimarisi (software architecture), bir sistemin geri alınması pahalı kararlarının kümesidir. “Pahalı” sözcüğü bu derste bir sayıya bağlandı: kararı bozmak için dokunulması gereken modül ve dosya sayısı. Mimari açıdan önemli karar (architecturally significant decision), bu sayı seçilmiş bir eşiğin üstünde kalan karardır; eşik yazılır ve kaynağı söylenir.

Aynı ölçü ikinci bir ayrımı da verir. Geri döndürülebilir karar, erişim alanı küçük olan karardır — kiosk ucunun ayrı bir dağıtım birimi olması gibi, dokuz dosyada başlayıp biten bir seçim. Geri döndürülemez karar diye anılan şey mutlak bir engel değil, alanı büyüdükçe geri alma maliyeti kod tabanının tamamına yaklaşan karardır. İki uç arasındaki fark bu modelde 215’e 9, yani 23,9 kattır.

Tanımın pratik sonucu şudur: bir kararın mimari olup olmadığı kararın konusuna bakarak söylenemez. Ödünç süresi kuralının nereden okunduğu küçük bir ayrıntı gibi görünür ve model kod tabanında 82 dosyaya dokunur; bir mimari biçemin seçimi büyük bir karar gibi görünür ve o seçimin gerçek ağırlığı da yine erişim alanından okunur. Mimari Biçemler kursunda kurulan katmanlı, altıgen, olay güdümlü ve mikroservis biçemleri bu müfredatta yeniden anlatılmayacak; bu kursun konusu biçemin kendisi değil, o biçemi seçen karardır.

Özet

  • Bir kararın gövdesi kodda karşılığı olan modül kümesidir; geri alma maliyeti ise gövdeye doğrudan ya da dolaylı bağımlı bütün modüllerin dosya sayısıdır.
  • Model kod tabanında (24 modül, 217 dosya) on bir karar ölçüldü; en pahalı kararın gövdesi üç dosya, erişim alanı 215 dosyaydı — 71,7 kat.
  • Eşik yazılmadan ayrım olmaz: kod tabanının yarısı kuralı dört kararı ayırdı (ortalamalar 163,5 ve 48,7 dosya), sıralamadaki en büyük boşluk kuralı iki kararı ayırdı; eşiğe en yakın karar sınırın bir dosya altında kaldı.
  • Gövde büyüklüğüne göre sıralamayla geri alma maliyetine göre sıralama 55 çiftin 31’inde ters düştü; gövdesi en büyük karar 142 dosyaya, gövdesi en küçüklerden biri 215 dosyaya dokunuyor.
  • Yazılım mimarisi, geri alınması pahalı kararların kümesidir; “bu karar mimaridir” cümlesi ölçüsüzdür, yerine dokunulan modül ve dosya sayısı yazılır.

Sonraki Adım

Bu ders kararları tek bir kod tabanı üzerinde ölçtü ve hepsini aynı düzlemde sıraladı. Oysa çıktıdaki kararların hepsi aynı yerde verilmez. Ayar değerlerinin nereden okunacağı tek bir uygulamanın içinde kalabilir; şubeler arası envanterin nasıl eşitleneceği birden çok uygulamayı ilgilendirir; dışarıdan alınan katalog sistemine tek bağlayıcı üzerinden erişilmesi ise bütün şubeleri ve bilgi işlem biriminin bütçesini bağlar. Sonraki ders aynı karar kümesini üç düzeye dağıtır — uygulama, çözüm ve kurumsal mimari — ve her düzeyin kaç kararı kapsadığını, kaç birimi etkilediğini sayar. Asıl ölçülecek olan şudur: bir karar yanlış düzeyde verildiğinde kaç birim habersiz kalır ve aynı karar kaç kez yeniden verilir.

İ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