İçeriğe geç
academia.sh

Ders 01 / 13

Sistem Tasarımı Nedir

Bir sistem tasarımının üç karar türüne ayrılması: bileşen sınırları, sınırdan geçen sözleşmeler ve niteliklere konan eşikler; eşiğin nereden geldiği sorusunun yanıtı olarak sayının üç sınıfının ayrılması ve sınıfsız sayının karar taşımadığının gösterilmesi.

İçindekiler

Mimari Biçemler kursu aynı gönderi kitaplığının düzenlenişlerini karşılaştırdı ve her karşılaştırmayı üç parçaya indirdi: nitelik senaryosu, eşik ve ölçülen değer. Eşikler o kursta verilmişti; “sınırdan geçen bayt en çok 100” gibi bir sayı alıştırmanın girdisiydi. Kursun son cümlesi bunu işaretledi: biçem seçimi burada da aynı üç parçayla savunulur, değişen yalnız eşiklerin nereden geldiğidir.

Bu kurs o soruyla açılır. Bir sistemde eşik keyfi değildir, bir yerden gelir ve nereden geldiği yazılabilir. Bu ders eşiğin bağlı olduğu karar türlerini ayırır ve kursun bütün derslerinde geçerli olacak kuralı kurar: bir tasarım metnindeki her sayının sınıfı söylenir.

Üç Karar Türü

Sistem tasarımı (system design), birden çok bileşene bölünmüş bir yazılımın bu bölünmeyi ve bölünmenin getirdiği kısıtları belirleme işidir. Bir programın parçaları birlikte çalışır ve birlikte durur; bir sistemin bileşenleri (component) ayrı ölçeklenebilir ve ayrı arızalanabilir. Bu iki cümle arasındaki fark, sistem tasarımının bütün sorularını üretir.

Bir tasarım üç tür karardan oluşur ve üçü ayrı ayrı gerekçelendirilir.

Bileşen kararı hangi işin hangi birimde durduğunu söyler. Ölçüsü basittir: bir bileşenin tek başına ölçeklenebilmesi ve durduğunda geri kalanın ne kadarının ayakta kalması.

Sözleşme kararı (contract) bileşen sınırından neyin geçtiğini söyler: hangi alanlar, hangi yönde, hangi biçimde. Sınırdan geçen her alan iki bileşen arasında bir bağdır ve sonradan değiştirilmesi ikisini birlikte değiştirmeyi gerektirir.

Nitelik kararı bir kalite niteliğine eşik koyar: takip sorgusunun yanıt süresi, kabul edilen olay kaybı oranı, gün sonu işinin bitmesi gereken saat. Kalite niteliği ve nitelik senaryosu Kalite ve Test Temelleri kursunun Kalite Nitelikleri dersinde kuruldu; burada yeniden tanımlanmazlar, eşiğin taşıyıcısı olarak kullanılırlar.

İlk iki karar türü tek makinede de verilir ve Mimari Biçemler kursu onları orada ölçtü. Üçüncüsü sistemi sistem yapan karardır, çünkü eşik yazılımın içinden değil dışından gelir: yükten, beklentiden ve arıza tolerans kararından.

Sistem Olarak Gönderi Takibi

Kurs boyunca tek bir sistem üzerinde çalışılır: gönderi takip ve ücretlendirme servisi. Kullanıcı bir takip numarasıyla gönderisinin durumunu sorar, taşıyıcı gönderi hareket ettikçe durum olayı gönderir, satıcı gün sonunda ücretlendirme ister. Üç akış birbirinden ayrılır çünkü üçü ayrı eşik ister: okuma akışı çok ve küçük, yazma akışı sürekli ve küçük, toplu akış seyrek ve büyüktür.

Tasarımın ilk hâli bir veri yapısı olarak yazılabilir. Bileşenler, sözleşmeler ve akışlar listelendiğinde tasarım hakkında sayılabilir şeyler ortaya çıkar.

// tasarim/sistem.mjs — gonderi takip ve ucretlendirme servisi: bilesenler, sozlesmeler ve uc akis
export const BILESEN = {
  "takip-ucu": "takip sorgusunu yanitlar",
  "olay-alici": "tasiyicidan gelen durum olayini kabul eder",
  "gonderi-deposu": "gonderi ve teslimat durumunu tutar",
  "tarife-kurali": "bir gonderinin ucretini hesaplar",
  "toplu-isci": "saticinin gun sonu ucretlendirmesini uretir",
};

export const SOZLESME = {
  "takip-istegi": { yon: "istemci -> takip-ucu", alan: ["takipNo"] },
  "takip-yaniti": { yon: "takip-ucu -> istemci", alan: ["takipNo", "durum", "bolge", "guncellenme"] },
  "durum-olayi": { yon: "tasiyici -> olay-alici", alan: ["takipNo", "durum", "rota", "zaman"] },
  "durum-okuma": { yon: "takip-ucu -> gonderi-deposu", alan: ["takipNo"] },
  "durum-yazma": { yon: "olay-alici -> gonderi-deposu", alan: ["takipNo", "durum", "rota", "zaman"] },
  "gun-taramasi": { yon: "toplu-isci -> gonderi-deposu", alan: ["sozlesmeNo", "gun"] },
  "ucret-istegi": { yon: "toplu-isci -> tarife-kurali", alan: ["agirlik", "bolge", "oran"] },
};

export const AKIS = {
  okuma: { ad: "takip sorgusu", bilesen: ["takip-ucu", "gonderi-deposu"],
    sozlesme: ["takip-istegi", "durum-okuma", "takip-yaniti"] },
  yazma: { ad: "durum olayi", bilesen: ["olay-alici", "gonderi-deposu"],
    sozlesme: ["durum-olayi", "durum-yazma"] },
  toplu: { ad: "gun sonu ucretlendirme", bilesen: ["toplu-isci", "gonderi-deposu", "tarife-kurali"],
    sozlesme: ["gun-taramasi", "ucret-istegi"] },
};

Bu listeden çıkan ilk ölçü tasarım yüzeyidir: kaç bileşen, kaç sözleşme, sözleşmelerin taşıdığı kaç alan.

// tasarim/yuzey.mjs — her akisin dokundugu bilesen, gectigi sozlesme ve tasidigi alan sayisi
import { BILESEN, SOZLESME, AKIS } from "./sistem.mjs";

console.log(`bilesen = ${Object.keys(BILESEN).length}, sozlesme = ${Object.keys(SOZLESME).length}`);
console.log(`${"akis".padEnd(23)}${"bilesen".padStart(8)}${"sozlesme".padStart(9)}${"alan".padStart(6)}`);
for (const a of Object.values(AKIS)) {
  const alan = a.sozlesme.reduce((t, s) => t + SOZLESME[s].alan.length, 0);
  console.log(`${a.ad.padEnd(23)}${String(a.bilesen.length).padStart(8)}${String(a.sozlesme.length).padStart(9)}${String(alan).padStart(6)}`);
}

for (const b of Object.keys(BILESEN)) {
  const akislar = Object.values(AKIS).filter((a) => a.bilesen.includes(b)).map((a) => a.ad);
  console.log(`${b.padEnd(16)} dokunan akis = ${akislar.length}${akislar.length > 1 ? ` (${akislar.join(", ")})` : ""}`);
}
bilesen = 5, sozlesme = 7
akis                    bilesen sozlesme  alan
takip sorgusu                 2        3     6
durum olayi                   2        2     8
gun sonu ucretlendirme        3        2     5
takip-ucu        dokunan akis = 1
olay-alici       dokunan akis = 1
gonderi-deposu   dokunan akis = 3 (takip sorgusu, durum olayi, gun sonu ucretlendirme)
tarife-kurali    dokunan akis = 1
toplu-isci       dokunan akis = 1

Son satırlar tasarımın en önemli olgusunu veriyor: beş bileşenin dördü tek akışa hizmet ediyor, gonderi-deposu ise üçüne birden. Bu bileşen üç ayrı eşiğin kesiştiği yerdir ve kursun ilerleyen konularında her yeni kısıt önce burada görünür. Bir tasarımın en kırılgan noktası, en çok akışın dokunduğu bileşendir.

Eşiğin Nereden Geldiği

Nitelik kararı bir sayı taşır ve o sayının bir kaynağı vardır. Kaynak üç sınıftan birine girer; hangisi olduğu metinde yazılmadıkça karar denetlenemez.

Varsayım, tasarımın girdisi olarak seçilen sayıdır: günlük etkin kullanıcı, kullanıcı başına takip sorgusu, kayıt boyutu. Doğruluğu tasarımın içinden gösterilemez, bu yüzden gerekçesi ve iki katına çıktığında ne olacağı yazılır.

Hesap, varsayımlardan aritmetikle çıkan sayıdır: saniyedeki istek, günlük veri artışı, gereken bant genişliği. Bir hesap ancak dayandığı varsayımlar kadar geçerlidir.

Ölçüm, bir düzenekten çıkan sayıdır: bu makinede koşturulan bir işlevin süresi, bir tamponun zirvesi. Ölçüm ortama bağlıdır ve bu bağlılık metinde işaretlenir.

Aşağıdaki betik dört kararı bu üç sınıfa göre denetler. Dördüncü karar bilinçli olarak sınıfsız bırakılmıştır.

// tasarim/karar.mjs — bir kararin yanindaki sayinin sinifi: varsayim, hesap ya da olcum
const SINIF = ["varsayim", "hesap", "olcum"];
const BUTCE_US = 50;                          // tarife cagrisi icin secilen butce (mikrosaniye)

// Iki varsayim; tam varsayim tablosu Kabaca Buyukluk Hesabi dersinde kurulur.
const V = { gunlukKullanici: 2_000_000, kullaniciBasinaSorgu: 6, gunlukGonderi: 400_000, gonderiBasinaOlay: 7 };
const sorgu = V.gunlukKullanici * V.kullaniciBasinaSorgu;
const olay = V.gunlukGonderi * V.gonderiBasinaOlay;

const KADEME = [[1, 3000], [5, 4800], [20, 9600]];
const BOLGE = { "34": 100, "06": 115, "35": 125 };
function tarife(g) {
  const kademe = KADEME.find(([k]) => g.agirlik <= k) ?? [0, 9600];
  const taban = Math.max(2500, Math.round((kademe[1] * BOLGE[g.bolge]) / 100));
  return taban - Math.round(taban * Math.min(g.oran, 0.4));
}

function ortancaMikrosaniye(kosu = 9) {
  const sureler = [];
  for (let i = 0; i < kosu; i += 1) {
    const bas = process.hrtime.bigint();
    tarife({ agirlik: 4, bolge: "35", oran: 0.15 });
    sureler.push(Number(process.hrtime.bigint() - bas) / 1000);
  }
  sureler.sort((a, b) => a - b);
  return sureler[Math.floor(kosu / 2)];
}

const KARAR = [
  { ad: "okuma yolu yazmadan ayri olceklenir", olcu: "okuma/yazma istek orani",
    sayi: (sorgu / olay).toFixed(2), sinif: "hesap" },
  { ad: "takip yaniti onbellekten karsilanir", olcu: "onbellek isabet orani",
    sayi: "0.90", sinif: "varsayim" },
  { ad: "tarife kurali surec icinde cagrilir", olcu: `${BUTCE_US} us butcesi`,
    sayi: ortancaMikrosaniye() < BUTCE_US ? "gecti" : "kaldi", sinif: "olcum" },
  { ad: "toplu is gece penceresinde kosar", olcu: "pencere saati",
    sayi: "4", sinif: null },
];

console.log(`${"karar".padEnd(37)}${"olcu".padEnd(25)}${"sayi".padStart(7)}  sinif`);
for (const k of KARAR) {
  console.log(`${k.ad.padEnd(37)}${k.olcu.padEnd(25)}${String(k.sayi).padStart(7)}  ${k.sinif ?? "-"}`);
}
const sinifsiz = KARAR.filter((k) => SINIF.includes(k.sinif) === false);
console.log(`\nsinifsiz sayi tasiyan karar = ${sinifsiz.length}: ${sinifsiz.map((k) => k.ad).join("; ")}`);
console.log(`gunluk takip sorgusu = ${sorgu}, gunluk durum olayi = ${olay}`);
console.log(`varsayim ikiye katlanirsa: kullanici basina sorgu 12 -> oran ${((sorgu * 2) / olay).toFixed(2)}`);
karar                                olcu                        sayi  sinif
okuma yolu yazmadan ayri olceklenir  okuma/yazma istek orani     4.29  hesap
takip yaniti onbellekten karsilanir  onbellek isabet orani       0.90  varsayim
tarife kurali surec icinde cagrilir  50 us butcesi              gecti  olcum
toplu is gece penceresinde kosar     pencere saati                  4  -

sinifsiz sayi tasiyan karar = 1: toplu is gece penceresinde kosar
gunluk takip sorgusu = 12000000, gunluk durum olayi = 2800000
varsayim ikiye katlanirsa: kullanici basina sorgu 12 -> oran 8.57

Dört satır dört ayrı okuma gerektiriyor. Okuma/yazma oranı 4.29 bir hesaptır: iki varsayımın bölümüdür ve son satır duyarlılığını gösteriyor — kullanıcı başına sorgu 6 yerine 12 olsaydı oran 8.57 olurdu. Önbellek isabet oranı 0.90 bir varsayımdır; gerekçesi aynı takip numarasının teslime kadar birkaç kez sorulmasıdır, doğruluğu ancak sistem çalıştığında sınanır. Tarife çağrısının bütçeyi geçmesi bir ölçümdür ve çıktıya ham süre değil yargı yazılmıştır, çünkü ham süre bu makineye ve çalışma zamanı sürümüne bağlıdır.

Dördüncü satırın sınıfı yoktur. “Toplu iş dört saatlik gece penceresinde koşar” cümlesi tasarım metninde makul görünür ama denetlenemez: dört saat bir varsayım mı, satıcının beklentisinden çıkan bir hesap mı, yoksa bir koşumda ölçülmüş bir süre mi belirsizdir. Sınıfsız sayı karar taşımaz — ölçülmemiş eksenin tartışmada ağırlığı olmadığı gibi.

Bileşen Kararı Nitelik Kararına Bağlıdır

Üç karar türü sırayla verilmez, birbirini belirler. Bileşen listesinin ilk hâli işlevsel beklentiden çıkar: takip sorgusunu yanıtlayan bir uç, olayı kabul eden bir alıcı, durumu tutan bir depo. Bu liste bir düzen değil, yalnız işin bölünüşüdür.

Bileşen sayısının bu listenin ötesine geçmesi nitelik kararlarından olur. Okuma akışının eşiği yazma akışının eşiğinden bağımsız tutulmak isteniyorsa iki akış ayrı ölçeklenebilir bileşenlere konur ve bileşen sayısı artar. Toplu işin gün sonu penceresi okuma akışının yanıt süresini bozmamalıysa toplu iş kendi birimine taşınır. Her ayırma sözleşme sayısını ve sınırdan geçen alan sayısını büyütür; Mimari Biçemler kursunda ölçülen bedel budur.

Sıralamanın yönü bu yüzden tersinden okunur: eşikler bilinmeden bileşen sayısı savunulamaz. Bir tasarımı “beş bileşen” diye anlatmak, hangi eşiğin hangi ayırmayı zorunlu kıldığını söylemedikçe bir karar değildir. Sonraki iki ders eşiğin kaynağını sırayla açar: önce gereksinimlerin nasıl toplandığı, sonra işlevsel olanla işlevsel olmayanın ayrı belirleyici olması.

Özet

  • Sistem tasarımı üç tür karardan oluşur: bileşen sınırları, sınırdan geçen sözleşmeler ve niteliklere konan eşikler; ilk ikisi tek makinede de verilir, üçüncüsü yükten ve beklentiden gelir.
  • Tasarım yüzeyi sayılabilir: örnek sistemde 5 bileşen, 7 sözleşme; takip sorgusu 3 sözleşme ve 6 alan, durum olayı 2 sözleşme ve 8 alan, gün sonu ücretlendirmesi 2 sözleşme ve 5 alan taşıyor.
  • En çok akışın dokunduğu bileşen en kırılgan noktadır: gonderi-deposu üç akışın da içinde, öteki dört bileşen birer akışta.
  • Bir kararın yanındaki sayı üç sınıftan birine girer: varsayım seçilir ve duyarlılığı yazılır, hesap varsayımlardan çıkar, ölçüm bir düzenekten çıkar ve ortama bağlılığı işaretlenir.
  • Sınıfsız sayı karar taşımaz; örnekte dört karardan biri sınıfsız kaldığı için denetlenemez sayıldı.
  • Bileşen sayısı işlevsel bölünüşten değil nitelik eşiklerinden büyür; eşik bilinmeden bileşen sayısı savunulamaz.

Sonraki Adım

Bu ders eşiğin bir kaynağı olduğunu söyledi ama kaynağı aramadı. Örnekteki beş bileşen ve yedi sözleşme hazır verildi; oysa gerçek bir tasarım işi tek satırlık bir istekle başlar: “satıcıların gönderilerini takip edebileceği ve gün sonunda ücretlendirilebileceği bir servis.” O satır bileşen adı içermez, sayı içermez, sınır içermez. Sonraki ders o satırdan bir tasarıma nasıl geçildiğini ele alır: hangi soruların sorulduğunu, yanıtsız kalan her sorunun neden bir varsayıma dönüştüğünü ve kapsamın daraltılmasının tasarım yüzeyinde ne kadar yer açtığını.

İ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