---
title: 'Bileşen Tabanlı Mimari'
source: 'https://academia.sh/tr/kurslar/mimari-bicemler/bilesen-tabanli-mimari'
course: 'Mimari Biçemler'
language: tr
updated: '2026-08-17T18:10:36+00:00'
license: 'CC BY-SA 4.0'
---

# Bileşen Tabanlı Mimari

Birimin halkadan yayınlanabilir bileşene taşınması: her bileşenin dışa açtığı ve kapsadığı ad sayısının sayılması, aynı dosyaların iki bölünmesinde birlikte yayın oranının karşılaştırılması, yüzeyi atlayan bir tüketicinin dışa açılan ad sayısını nasıl yükselttiğinin ölçülmesi ve yeni bir yeteneğin düzenlettiği, eklettiği, yeniden yayınlattığı parçaların sayılması.

Bir önceki dersin bütün ölçüleri halka üzerindeydi ve halka bir kural birimidir. Hangi dosya
kümesinin birlikte paketlendiği, hangisinin ayrı sürümlenebildiği ve bir tüketicinin tek bir
yetenek için kaç ada bağlandığı o ölçülerde görünmüyordu.

Bu ders birimi değiştirir. **Bileşen** (component) burada ayrı paketlenen, ayrı sürümlenen ve
dışa açtığı adları bildirilmiş bir dosya kümesidir; Bileşen Tabanlı Geliştirme kursundaki
bileşen ise ekranda bir parçayı çizen bir görünüm işleviydi — aynı sözcüğün iki ayrı birimi.
Ölçüler de değişir: bir bileşenin dışa açtığı ad sayısı, kapsadığı ad sayısı, iki bileşenin
birlikte yayınlanma oranı ve yeni bir yeteneğin düzenlettiği dosya sayısı.

## Yayın Birimi Olarak Bileşen

Bileşen sınırının nereden geçeceği Tasarım İlkeleri kursundaki Bileşen Uyum İlkeleri ve Bileşen
Bağlaşım İlkeleri derslerinde kurulmuştu: yeniden kullanım–yayın eşdeğerliği, ortak kapanış,
ortak yeniden kullanım ve kararlı bağımlılıklar. Burada o ilkeler yeniden anlatılmıyor, bir
düzen olarak uygulanıyor.

Düzenin taşıyıcı kararı şudur: her bileşen dışa açtığı adları tek bir dosyada bildirir, geri
kalanı kendi içinde tutar. Bu karar iki sayıyı ayırır. **Dışa açılan ad**, bileşenin dışındaki
bir dosyanın o bileşenden ithal ettiği addır. **Kapsanan ad**, bileşenin ihraç ettiği ama
dışından kimsenin ithal etmediği addır. İkincisi serbestlik payıdır: kapsanan bir adı
değiştirmek hiçbir tüketiciyi kırmaz.

## Kitaplığın Bileşenleri

Kitaplık beş bileşene bölünüyor. Üçü ücretlendirme bağlamındadır (`ortak`, `bolge`, `ucret`),
biri teslimat operasyonundadır (`rota`), biri de ikisini tek bir istekte birleştirir (`masa`).
Her bileşenin `dis.mjs` dosyası o bileşenin yüzeyini bildirir.

```sh
mkdir -p bilesenler/ortak bilesenler/bolge bilesenler/ucret bilesenler/rota bilesenler/masa
```

```js
// bilesenler/ortak/dis.mjs — ortak bileseni: para birimi cevrimi
export const kurus = (lira) => Math.round(lira * 100);
export const yazi = (k) => `${(k / 100).toFixed(2)} TL`;
```

```js
// bilesenler/bolge/esleme.mjs — bolge bileseni ici: posta kodu esdegeri ve katsayilar
export const POSTA_BOLGE = { 34: "yakin", "06": "orta", 35: "orta", 65: "uzak" };
export const KATSAYI = { yakin: 1, orta: 1.35, uzak: 1.8 };
export const bolgeBul = (postaKodu) => POSTA_BOLGE[postaKodu.slice(0, 2)] ?? "uzak";
```

```js
// bilesenler/bolge/dis.mjs — bolge bileseninin disa actigi yuzey
import { KATSAYI, bolgeBul } from "./esleme.mjs";

export const bolge = (postaKodu) => bolgeBul(postaKodu);
export const katsayi = (postaKodu) => KATSAYI[bolgeBul(postaKodu)];
```

```js
// bilesenler/ucret/tarife.mjs — ucret bileseni ici: kademeler, asgari tutar, yuvarlama
import { kurus } from "../ortak/dis.mjs";

export const KADEMELER = [[1, 49.9], [5, 84.9], [15, 149.9], [30, 249.9]]
  .map(([ustAgirlik, lira]) => ({ ustAgirlik, ucret: kurus(lira) }));
export const ASGARI = kurus(39.9);
export const yuvarla = (k) => Math.round(k / 50) * 50;
```

```js
// bilesenler/ucret/kural.mjs — ucret bileseni ici: ucret kurali
import { ASGARI, KADEMELER, yuvarla } from "./tarife.mjs";

export const ucret = (gonderi, katsayi) => {
  const kademe = KADEMELER.find((k) => gonderi.agirlik <= k.ustAgirlik);
  if (kademe === undefined) throw new RangeError("agirlik kademesi yok");
  return Math.max(yuvarla(kademe.ucret * katsayi), ASGARI);
};
```

```js
// bilesenler/ucret/dis.mjs — ucret bileseninin disa actigi yuzey
import { katsayi } from "../bolge/dis.mjs";
import { yazi } from "../ortak/dis.mjs";
import { ucret } from "./kural.mjs";

export const teklifVer = (gonderi) => {
  const tutar = ucret(gonderi, katsayi(gonderi.postaKodu));
  return { tutar, gosterim: yazi(tutar) };
};
```

```js
// bilesenler/rota/tasiyici.mjs — rota bileseni ici: tasiyici listesi ve kapsamlari
export const TASIYICILAR = [
  { ad: "kuzey", kapsam: ["yakin", "orta"], aktarma: 1 },
  { ad: "genel", kapsam: ["yakin", "orta", "uzak"], aktarma: 2 },
];
export const uygunlar = (b) => TASIYICILAR.filter((t) => t.kapsam.includes(b));
```

```js
// bilesenler/rota/dis.mjs — rota bileseninin disa actigi yuzey
import { bolge } from "../bolge/dis.mjs";
import { uygunlar } from "./tasiyici.mjs";

export const rotaKur = (gonderi) => {
  const secilen = uygunlar(bolge(gonderi.postaKodu))
    .sort((x, y) => x.aktarma - y.aktarma)[0];
  if (secilen === undefined) throw new RangeError("uygun tasiyici yok");
  return { tasiyici: secilen.ad, aktarma: secilen.aktarma };
};
```

```js
// bilesenler/masa/dis.mjs — masa bileseni: dis istegi iki bilesene dagitir
import { rotaKur } from "../rota/dis.mjs";
import { teklifVer } from "../ucret/dis.mjs";

export const istek = (gonderi) => ({ ...teklifVer(gonderi), ...rotaKur(gonderi) });
```

## Dışa Açılan ve Kapsanan Adlar

Araç her dosyanın ihraç ettiği adları ve ithal satırlarındaki adları okur, sonra aynı dosyaları
iki bölünmeye göre gruplar: **yeteneğe** göre (dizin adı) ve **role** göre (`dis.mjs` yüzey,
`kural.mjs` kural, geri kalanı veri). Dosyalar ve ithaller iki bölünmede aynıdır; değişen yalnız
sınırın nereden geçtiğidir.

```js
// yuzey-olc.mjs — her birimin ihrac ettigi, disa actigi ve kapsadigi ad sayisini olcer
import { readdirSync, readFileSync } from "node:fs";
import { join, normalize } from "node:path";

const IHRAC = /^export const (\w+)/gm;
const ITHAL = /^import\s*\{([^}]*)\}\s*from\s+"([^"]+)"/gm;
export const BOLUNME = {
  yetenek: (f) => f.dizin,
  rol: (f) => (f.ad === "dis.mjs" ? "arayuz" : f.ad === "kural.mjs" ? "kural" : "veri"),
};

export function oku(kok) {
  const dosyalar = [];
  for (const dizin of readdirSync(kok).sort()) {
    for (const ad of readdirSync(join(kok, dizin)).sort()) {
      const metin = readFileSync(join(kok, dizin, ad), "utf8");
      dosyalar.push({
        yol: `${dizin}/${ad}`, dizin, ad,
        ihrac: [...metin.matchAll(IHRAC)].map((m) => m[1]),
        ithal: [...metin.matchAll(ITHAL)].map((m) => ({
          adlar: m[1].split(",").map((s) => s.trim()).filter((s) => s !== ""),
          hedef: normalize(join(dizin, m[2])),
        })),
      });
    }
  }
  return dosyalar;
}

if (import.meta.filename === process.argv[1]) {
  const kok = process.argv[2];
  const dosyalar = oku(kok);
  const toplamIhrac = dosyalar.reduce((n, f) => n + f.ihrac.length, 0);
  console.log(`${kok}/  dosya = ${dosyalar.length}, toplam ihrac = ${toplamIhrac}`);
  for (const [bad, birimi] of Object.entries(BOLUNME)) {
    const ihrac = new Map();
    const disa = new Map();
    for (const f of dosyalar) {
      ihrac.set(birimi(f), (ihrac.get(birimi(f)) ?? 0) + f.ihrac.length);
      disa.set(birimi(f), disa.get(birimi(f)) ?? new Set());
    }
    for (const f of dosyalar) {
      for (const i of f.ithal) {
        const hedef = dosyalar.find((d) => d.yol === i.hedef);
        if (hedef === undefined || birimi(hedef) === birimi(f)) continue;
        for (const n of i.adlar) disa.get(birimi(hedef)).add(n);
      }
    }
    console.log(`  ${bad} bolunmesi (${ihrac.size} birim)`);
    let td = 0;
    for (const b of [...ihrac.keys()].sort()) {
      const d = disa.get(b).size;
      td += d;
      console.log(`    ${b.padEnd(8)} ihrac ${String(ihrac.get(b)).padStart(2)}`
        + `  disa acilan ${String(d).padStart(2)}  kapsanan ${String(ihrac.get(b) - d).padStart(2)}`);
    }
    console.log(`    ${"toplam".padEnd(8)} ihrac ${String(toplamIhrac).padStart(2)}`
      + `  disa acilan ${String(td).padStart(2)}  kapsanan ${String(toplamIhrac - td).padStart(2)}`);
  }
}
```

```sh
node yuzey-olc.mjs bilesenler
```

```
bilesenler/  dosya = 9, toplam ihrac = 16
  yetenek bolunmesi (5 birim)
    bolge    ihrac  5  disa acilan  2  kapsanan  3
    masa     ihrac  1  disa acilan  0  kapsanan  1
    ortak    ihrac  2  disa acilan  2  kapsanan  0
    rota     ihrac  3  disa acilan  1  kapsanan  2
    ucret    ihrac  5  disa acilan  1  kapsanan  4
    toplam   ihrac 16  disa acilan  6  kapsanan 10
  rol bolunmesi (3 birim)
    arayuz   ihrac  7  disa acilan  1  kapsanan  6
    kural    ihrac  1  disa acilan  1  kapsanan  0
    veri     ihrac  8  disa acilan  6  kapsanan  2
    toplam   ihrac 16  disa acilan  8  kapsanan  8
```

On altı ihraç edilen addan yetenek bölünmesinde 6'sı dışa açılıyor ve 10'u kapsanıyor; rol
bölünmesinde 8'i dışa açılıyor, 8'i kapsanıyor. Rol bölünmesinde `veri` birimi sekiz adından
altısını dışa açmak zorundadır, çünkü kuralı ve yüzeyi ondan ayıran sınır tam o adların geçtiği
yerdedir. `ucret` bileşeni ise beş adından dördünü içinde tutuyor; dışarıya yalnız `teklifVer`
çıkıyor. `masa` bileşeninin dışa açılan adı 0: ona bağlı başka bir bileşen yok, yani Bileşen
Bağlaşım İlkeleri dersindeki kararsızlık ölçüsünün üst ucundadır.

## Yüzeyi Atlayan Tüketici

Dışa açılan ad sayısı bileşenin tek başına özelliği değil, bileşen ile tüketicilerinin ortak
özelliğidir. Aşağıdaki blok ağacın bir kopyasına yüzeyleri atlayıp komşularının içine bağlanan
bir bileşen ekliyor.

```sh
cp -r bilesenler derin-agac
mkdir -p derin-agac/derin
cat > derin-agac/derin/dis.mjs <<'EOF'
// derin-agac/derin/dis.mjs — yuzeyi atlayip komsularin icine baglanan tuketici
import { KATSAYI, bolgeBul } from "../bolge/esleme.mjs";
import { yazi } from "../ortak/dis.mjs";
import { uygunlar } from "../rota/tasiyici.mjs";
import { ucret } from "../ucret/kural.mjs";

export const istek = (gonderi) => {
  const b = bolgeBul(gonderi.postaKodu);
  const tutar = ucret(gonderi, KATSAYI[b]);
  return { tutar, gosterim: yazi(tutar), tasiyici: uygunlar(b)[0].ad };
};
EOF
node yuzey-olc.mjs derin-agac | head -9
node -e "import('./derin-agac/derin/dis.mjs').then(m => console.log('derin tuketici ' + JSON.stringify(m.istek({ agirlik: 3, postaKodu: '06800' }))))"
```

```
derin-agac/  dosya = 10, toplam ihrac = 17
  yetenek bolunmesi (6 birim)
    bolge    ihrac  5  disa acilan  4  kapsanan  1
    derin    ihrac  1  disa acilan  0  kapsanan  1
    masa     ihrac  1  disa acilan  0  kapsanan  1
    ortak    ihrac  2  disa acilan  2  kapsanan  0
    rota     ihrac  3  disa acilan  2  kapsanan  1
    ucret    ihrac  5  disa acilan  2  kapsanan  3
    toplam   ihrac 17  disa acilan 10  kapsanan  7
derin tuketici {"tutar":11450,"gosterim":"114.50 TL","tasiyici":"kuzey"}
```

Tüketici doğru çalışıyor ve yüzeyden geçen yolla aynı tutarı üretiyor: 11450 kuruş. Değişen şey
sınırdır. Dışa açılan ad 6'dan 10'a çıktı, kapsanan ad 10'dan 7'ye indi; `bolge` bileşeninin
kapsanan adı 3'ten 1'e düştü. Tek bir dosya, dokunmadığı üç bileşenin serbestlik payını harcadı.
Ortak yeniden kullanım ilkesinin ölçülebilir karşılığı budur: derin ithal, tüketiciyi bileşenin
sürüm sözleşmesinin dışında kalan adlara bağlar.

## Birlikte Yayın Oranı

İkinci ölçü yayınla ilgilidir. Bir dosya değiştiğinde onu ithal eden her dosya yeniden sınanır ve
yayınlanır, bu yüzden bir değişikliğin yeniden yayınlattığı birim kümesi ithal grafiğinden
çıkarılabilir. İki birimin **birlikte yayın oranı**, ikisini birlikte yayınlatan dosya sayısının
en az birini yayınlatan dosya sayısına oranıdır.

```js
// yayin-olc.mjs — bir dosya degisince hangi birimlerin yeniden yayinlanmasi gerektigini sayar
import { BOLUNME, oku } from "./yuzey-olc.mjs";

const [kok, hedef] = process.argv.slice(2);
const dosyalar = oku(kok);
const ithalEden = new Map(dosyalar.map((f) => [f.yol, []]));
for (const f of dosyalar) {
  for (const i of f.ithal) if (ithalEden.has(i.hedef)) ithalEden.get(i.hedef).push(f.yol);
}

function kapanis(yol) {
  const gorulen = new Set([yol]);
  for (const y of gorulen) for (const k of ithalEden.get(y)) gorulen.add(k);
  return gorulen;
}

for (const [bad, birimi] of Object.entries(BOLUNME)) {
  const birimOf = new Map(dosyalar.map((f) => [f.yol, birimi(f)]));
  const etkilenen = dosyalar.map((f) => new Set([...kapanis(f.yol)].map((y) => birimOf.get(y))));
  const birimler = [...new Set(birimOf.values())].sort();
  console.log(`${kok}/ ${bad} bolunmesi (${birimler.length} birim)`);
  for (const b of birimler) {
    console.log(`  ${b.padEnd(8)} dosya ${dosyalar.filter((f) => birimi(f) === b).length}`
      + `  yeniden yayinlatan dosya ${etkilenen.filter((e) => e.has(b)).length} / ${dosyalar.length}`);
  }
  let toplam = 0, cift = 0, enYuksek = ["yok", 0];
  for (let i = 0; i < birimler.length; i += 1) {
    for (let j = i + 1; j < birimler.length; j += 1) {
      const [a, b] = [birimler[i], birimler[j]];
      const oran = etkilenen.filter((e) => e.has(a) && e.has(b)).length
        / etkilenen.filter((e) => e.has(a) || e.has(b)).length;
      toplam += oran;
      cift += 1;
      if (oran > enYuksek[1]) enYuksek = [`${a}+${b}`, oran];
    }
  }
  console.log(`  cift ${cift}, ortalama birlikte yayin orani ${(toplam / cift).toFixed(2)}`
    + `, en yuksek ${enYuksek[0]} ${enYuksek[1].toFixed(2)}`);
  if (hedef !== undefined) {
    const kume = new Set([...kapanis(hedef)].map((y) => birimOf.get(y)));
    const dosyaSayisi = dosyalar.filter((f) => kume.has(birimi(f))).length;
    console.log(`  ${hedef} degisince: ${kume.size} / ${birimler.length} birim,`
      + ` ${dosyaSayisi} / ${dosyalar.length} dosya  (${[...kume].sort().join(", ")})`);
  }
}
```

```sh
node yayin-olc.mjs bilesenler
```

```
bilesenler/ yetenek bolunmesi (5 birim)
  bolge    dosya 2  yeniden yayinlatan dosya 2 / 9
  masa     dosya 1  yeniden yayinlatan dosya 9 / 9
  ortak    dosya 1  yeniden yayinlatan dosya 1 / 9
  rota     dosya 2  yeniden yayinlatan dosya 4 / 9
  ucret    dosya 3  yeniden yayinlatan dosya 6 / 9
  cift 10, ortalama birlikte yayin orani 0.27, en yuksek masa+ucret 0.67
bilesenler/ rol bolunmesi (3 birim)
  arayuz   dosya 5  yeniden yayinlatan dosya 9 / 9
  kural    dosya 1  yeniden yayinlatan dosya 3 / 9
  veri     dosya 3  yeniden yayinlatan dosya 4 / 9
  cift 3, ortalama birlikte yayin orani 0.39, en yuksek arayuz+veri 0.44
```

Ortalama birlikte yayın oranı yetenek bölünmesinde 0.27, rol bölünmesinde 0.39. Daha keskin olan
sayı tablodadır: iki bölünmede de bir birim dokuz dosyanın dokuzunda yeniden yayınlanıyor, ama
yetenek bölünmesinde o birim 1 dosyalık `masa`, rol bölünmesinde 5 dosyalık `arayuz`. Her
değişiklikte yeniden yayınlanan parça beş katına çıkıyor. `bolge` bileşeni ise dokuz dosyanın
yalnız ikisinde yeniden yayınlanıyor; ortak kapanış ilkesinin karşılığı bu satırdır.

## Yeni Bir Yetenek

Üçüncü ölçü uzantı maliyetidir. Ücretlendirmeye yakıt ek ücreti ekleniyor: oran kendi dosyasına
giriyor, kural onu uyguluyor.

```sh
cp -r bilesenler yakitli
cat > yakitli/ucret/yakit.mjs <<'EOF'
// yakitli/ucret/yakit.mjs — ucret bileseni ici: yakit ek ucreti orani
export const YAKIT_ORANI = 0.06;
export const yakitEkle = (k) => k * (1 + YAKIT_ORANI);
EOF
cat > yakitli/ucret/kural.mjs <<'EOF'
// yakitli/ucret/kural.mjs — ucret bileseni ici: ucret kurali, yakit ek ucretiyle
import { ASGARI, KADEMELER, yuvarla } from "./tarife.mjs";
import { yakitEkle } from "./yakit.mjs";

export const ucret = (gonderi, katsayi) => {
  const kademe = KADEMELER.find((k) => gonderi.agirlik <= k.ustAgirlik);
  if (kademe === undefined) throw new RangeError("agirlik kademesi yok");
  return Math.max(yuvarla(yakitEkle(kademe.ucret * katsayi)), ASGARI);
};
EOF
diff -rq bilesenler yakitli
node yuzey-olc.mjs yakitli | head -8
node yayin-olc.mjs yakitli ucret/kural.mjs | grep degisince
node -e "import('./bilesenler/masa/dis.mjs').then(m => console.log('yakitsiz ' + JSON.stringify(m.istek({ agirlik: 3, postaKodu: '06800' }))))"
node -e "import('./yakitli/masa/dis.mjs').then(m => console.log('yakitli  ' + JSON.stringify(m.istek({ agirlik: 3, postaKodu: '06800' }))))"
```

```
Files bilesenler/ucret/kural.mjs and yakitli/ucret/kural.mjs differ
Only in yakitli/ucret: yakit.mjs
yakitli/  dosya = 10, toplam ihrac = 18
  yetenek bolunmesi (5 birim)
    bolge    ihrac  5  disa acilan  2  kapsanan  3
    masa     ihrac  1  disa acilan  0  kapsanan  1
    ortak    ihrac  2  disa acilan  2  kapsanan  0
    rota     ihrac  3  disa acilan  1  kapsanan  2
    ucret    ihrac  7  disa acilan  1  kapsanan  6
    toplam   ihrac 18  disa acilan  6  kapsanan 12
  ucret/kural.mjs degisince: 2 / 5 birim, 5 / 10 dosya  (masa, ucret)
  ucret/kural.mjs degisince: 2 / 3 birim, 6 / 10 dosya  (arayuz, kural)
yakitsiz {"tutar":11450,"gosterim":"114.50 TL","tasiyici":"kuzey","aktarma":1}
yakitli  {"tutar":12150,"gosterim":"121.50 TL","tasiyici":"kuzey","aktarma":1}
```

Değişiklik bir dosyayı düzenledi ve bir dosya ekledi. `ucret` bileşeninin ihraç ettiği ad 5'ten
7'ye çıktı ama dışa açılan adı 1'de kaldı: yeni iki ad kapsanan tarafa yazıldı, yüzey değişmedi
ve hiçbir tüketici düzenlenmedi. Yeniden yayınlanan parça yetenek bölünmesinde 5 birimin 2'si ve
10 dosyanın 5'i, rol bölünmesinde 3 birimin 2'si ve 10 dosyanın 6'sı. Teslimat operasyonu tarafına
dokunulmadı: iki koşumda `tasiyici` ve `aktarma` alanları aynı kaldı, yalnız tutar 11450'den
12150'ye çıktı.

Bedelin sayısı da burada. Beş bileşen beş yüzey dosyası ve beş sürüm demektir; tek ad ihraç eden
`masa` için bile ayrı bir yayın birimi tutuluyor. Yüzeyi daraltmak kapsanan ad sayısını yükseltir,
bileşen sayısını artırmak yayınlanacak birim sayısını yükseltir — sürdürülebilirlik ile dağıtımın
basitliği arasındaki ödünleşim bu iki sayıdır.

## Özet

- Bileşen burada ayrı paketlenen, ayrı sürümlenen ve dışa açtığı adları bir yüzey dosyasında
  bildirilmiş bir dosya kümesidir; kapsanan ad sayısı o bileşenin serbestlik payıdır.
- Aynı dokuz dosya yeteneğe göre bölündüğünde 16 addan 6'sı dışa açılıyor, role göre bölündüğünde
  8'i; `ucret` beş adından dördünü, `veri` sekiz adından yalnız ikisini içinde tutuyor.
- Yüzeyleri atlayan tek bir tüketici bileşeni dışa açılan adı 6'dan 10'a çıkardı ve `bolge`
  bileşeninin kapsanan adını 3'ten 1'e indirdi; ürettiği tutar yüzeyden geçen yolla aynıydı.
- Ortalama birlikte yayın oranı yeteneğe göre 0.27, role göre 0.39; her değişiklikte yeniden
  yayınlanan birim yetenek bölünmesinde 1 dosyalık, rol bölünmesinde 5 dosyalık.
- Yakıt ek ücreti 1 dosya düzenletti, 1 dosya eklettirdi, dışa açılan adı 6'da bıraktı ve yetenek
  bölünmesinde 5 birimin 2'sini, rol bölünmesinde 3 birimin 2'sini yeniden yayınlattı.

## Sonraki Adım

Bileşen sınırı yeniden kullanım birimini verdi: `ucret` bileşenine yeni bir yetenek eklendiğinde
yüzey değişmedi ve dört bileşene dokunulmadı. Ama bu ölçülerin hepsi **yayın** zamanındadır. Bu
birimlerden hangisi program **koşarken** takılıp çıkarılabilir? `masa` bileşeni iki bileşeni adıyla
ithal ediyor, dolayısıyla üçüncü bir yeteneği eklemek için yine düzenlenmesi gerekir; bileşen
sınırı bu soruyu yanıtlamıyor. Sonraki ders birimleri iki sınıfa böler: değişmeyen ve tek başına
koşabilen bir **çekirdek** ile ona kayıtla takılan **eklentiler**. Ölçüler de değişir: çekirdeğin
eklentiler hakkında bildiği ad sayısı, yeni bir eklentinin çekirdekte düzenlettiği satır sayısı ve
eklentisiz çekirdeğin koşup koşmadığı.
