---
title: Modülerleştirme
source: 'https://academia.sh/tr/kurslar/ios-uretim/modulerlestirme'
course: 'Mimari, Test ve Yayınlama'
language: tr
updated: '2026-10-21T05:03:07+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Uygulamayı paket sınırlarıyla modüllere bölmenin neyi değiştirdiğini söyler'
    - 'Modül bağımlılıklarını bir grafik olarak kurup çözülüp çözülmediğini ve döngü olup olmadığını denetler'
    - 'Derlemenin geçmesine bakan denetimin modül sınırı ihlaline karşı kör kaldığını ölçer'
    - 'Değişen modülün yeniden derlenecek bağımlılarını iş birimi olarak sayıp bütçeye bağlar'
---

# Modülerleştirme

Uygulamanın paket sınırlarıyla modüllere bölünmesi, özellik modüllerinin yalnız çekirdek modüle bağlanması kuralı ve derlemenin geçmesine bakan denetimin sınır ihlaline karşı körlüğü.

Dördüncü derste bağımlılığı dışarıdan vermeyi öğrendik: nesneler birbirini kurmak yerine bir sözleşmeyle tanır. Aynı fikir bir basamak yukarıda da geçerlidir. Uygulamanın tamamı tek bir hedefte duruyorsa her dosya her dosyayı görebilir; hiçbir kural bir görünümün ağ katmanını doğrudan çağırmasını ya da bir özelliğin başka bir özelliğin iç tiplerine uzanmasını engellemez.

Modülerleştirme kodu paketlere böler ve her paketin yalnız dışarıya açtığı yüzeyi göstermesini sağlar. Hangi modülün hangisine bağlanabileceği ise kuraldır. Bu dersin kuralı şudur: özellik modülleri yalnız çekirdek modüle bağlanır, birbirine bağlanmaz. Kusur bu kuralın çiğnenmesidir: bir özellik modülü başka bir özellik modülüne doğrudan bağlanır. Çekirdek önceki derslerdeki gibi birebir gelir.

```swift
// Separation.swift — bir denetimin dogru surumu kusurlu surumden ayirip ayirmadigini siniflandirir
struct Contrast<Output: CustomStringConvertible> {
    let label: String
    let correct: Output
    let faulty: Output
    let passesCorrect: Bool
    let passesFaulty: Bool

    var separation: String {
        switch (passesCorrect, passesFaulty) {
        case (true, false): return "ayiran"
        case (true, true): return "kor"
        case (false, false): return "gurultulu"
        case (false, true): return "ters"
        }
    }

    func describe() -> String {
        let onCorrect = passesCorrect ? "gecti" : "kaldi"
        let onFaulty = passesFaulty ? "gecti" : "kaldi"
        print("\(label) | dogru=\(correct)/\(onCorrect) kusurlu=\(faulty)/\(onFaulty) -> \(separation)")
        return separation
    }
}

func contrast<Output>(_ label: String, correct: Output, faulty: Output, check: (Output) -> Bool) -> Contrast<Output> {
    Contrast(label: label, correct: correct, faulty: faulty, passesCorrect: check(correct), passesFaulty: check(faulty))
}

func countSeparations(_ results: [String]) {
    for name in ["ayiran", "kor", "gurultulu", "ters"] {
        print("\(name): \(results.filter { $0 == name }.count)")
    }
}
```

Çekirdek bu derste de yeniden tanımlanmaz. Ayrım adları aynıdır.

## Paket Sınırı

Bir modül, paket yöneticisinin bir hedefidir. Hedefler arasındaki bağımlılıklar paket tanım dosyasında yazılır ve paket yöneticisi o dosyayı okuyarak hangi hedefin hangisini içe alabileceğine karar verir. Bir modülün `internal` bırakılan ögeleri başka modüllerden görünmez; `public` ya da `open` ile açılanlar sınırı geçer; aynı paketin modülleri arasında `package` da yeter.

```swift
// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "Shop",
    targets: [
        .target(name: "Core"),
        .target(name: "Feed", dependencies: ["Core"]),
        .target(name: "Profile", dependencies: ["Core"]),
        .target(name: "Settings", dependencies: ["Core"]),
        .target(name: "App", dependencies: ["Feed", "Profile", "Settings", "Core"]),
    ]
)
```

Bu paket tanımı çalıştırılmaz; paket yöneticisi ve derleme platformun aracıdır ve ders için çıktı iddia etmez. Yapısı kuralı gösterir: `Feed`, `Profile` ve `Settings` yalnız `Core`'a bağlanır, `App` hepsini birleştirir. Kusurlu sürümde tek bir satır değişir.

```swift
        .target(name: "Feed", dependencies: ["Core", "Profile"]),
```

`Feed` artık `Profile`'ı da içe alabilir. Derleme bu satırı reddetmez: `Profile` bir hedef olarak vardır, aralarında döngü yoktur. Bu dersin sorusu tam olarak buradadır: derleyicinin kabul ettiği bir bağımlılık, kuralın kabul ettiği bir bağımlılık olmayabilir.

## Bağımlılık Grafiği

Model paket tanımını bir grafiğe indirger: her modülün adı ve bağlandığı modüllerin listesi. Grafik üç soruya yanıt verir: her bağımlılık gerçekten bir modüle işaret ediyor mu, grafikte döngü var mı ve bir modül değişince hangi modüller yeniden derlenmek zorunda. Üçüncü soru sınırlayıcı ölçümde kullanılır. `Modules.swift` bir yardımcı dosyadır ve çıktı vermez.

```swift
// Modules.swift — modul bagimlilik grafigi
struct ModuleGraph {
    let edges: [String: [String]]

    var resolves: Bool {
        edges.values.allSatisfy { deps in deps.allSatisfy { edges[$0] != nil } }
    }

    var isAcyclic: Bool {
        func visit(_ name: String, _ seen: Set<String>) -> Bool {
            if seen.contains(name) { return false }
            return (edges[name] ?? []).allSatisfy { visit($0, seen.union([name])) }
        }
        return edges.keys.allSatisfy { visit($0, []) }
    }

    func dependents(of module: String) -> [String] {
        var found: Set<String> = []
        var queue = [module]
        while let current = queue.popLast() {
            for (name, deps) in edges where deps.contains(current) && !found.contains(name) {
                found.insert(name)
                queue.append(name)
            }
        }
        return found.sorted()
    }
}

func shopGraph(faulty: Bool) -> ModuleGraph {
    ModuleGraph(edges: [
        "core": [],
        "feed": faulty ? ["core", "profile"] : ["core"],
        "profile": ["core"],
        "settings": ["core"],
        "app": ["feed", "profile", "settings", "core"],
    ])
}

func featureViolations(_ graph: ModuleGraph) -> String {
    let broken = ["feed", "profile", "settings"].flatMap { feature in
        (graph.edges[feature] ?? []).filter { $0 != "core" }.map { "\(feature)->\($0)" }
    }
    return broken.isEmpty ? "yok" : broken.joined(separator: ",")
}
```

`resolves` her bağımlılığın grafikte bir modül olarak bulunduğunu söyler, `isAcyclic` döngüsüzlüğü, `featureViolations` ise kuralı uygular: özellik modüllerinin çekirdek dışında bağlandığı her modülü `özellik->hedef` biçiminde listeler, hiç yoksa `yok` döner. Üç denetim aynı iki grafiğe uygulanır; ilk ikisi derlemenin bakacağı şeye bakar, üçüncüsü kurala bakar.

```swift
// main.swift — derleme mi, kural mi
let good = shopGraph(faulty: false)
let bad = shopGraph(faulty: true)

let compiles = contrast("derleme-geciyor", correct: good.resolves, faulty: bad.resolves) { $0 }.describe()
let acyclic = contrast("dongu-yok", correct: good.isAcyclic, faulty: bad.isAcyclic) { $0 }.describe()
let rule = contrast("ozellik-yalniz-cekirdege-bagli", correct: featureViolations(good), faulty: featureViolations(bad)) { $0 == "yok" }.describe()

countSeparations([compiles, acyclic, rule])
```

```text
derleme-geciyor | dogru=true/gecti kusurlu=true/gecti -> kor
dongu-yok | dogru=true/gecti kusurlu=true/gecti -> kor
ozellik-yalniz-cekirdege-bagli | dogru=yok/gecti kusurlu=feed->profile/kaldi -> ayiran
ayiran: 1
kor: 2
gurultulu: 0
ters: 0
```

Çıktı Swift 6.4'te sandbox'ta üç kez koşturuldu ve üçünde de aynıdır. Üç denetimin biri ayıran, ikisi kör çıktı.

`derleme-geciyor` bağımlılıkların çözülüp çözülmediğine bakar. `feed -> profile` bağı gerçek bir modüle işaret ettiği için çözülür ve iki sürümde de `true` çıkar: denetim kördür. `dongu-yok` döngü arar. `feed -> profile` ve `profile -> core` bir döngü kurmaz, bu yüzden bu da iki sürümde geçer ve kördür. İkisi de yeşildir ve ikisi de yanıltıcıdır: kusur grafiğin geçerliliğinde değil, grafiğin kurala uygunluğundadır.

`ozellik-yalniz-cekirdege-bagli` kuralı uygular. Doğru sürümde ihlal yoktur ve `yok` çıkar. Kusurlu sürümde `feed->profile` listelenir ve denetim kalır. Bu ayıran olur, ve ayırmasının nedeni derleme kadar geniş bir soru sormak yerine kuralın kendi sorusunu sormasıdır. Derleme "bu çalışır mı" der; kural "bu izinli mi" der. Modülerleştirmenin değeri ikinci sorudadır.

## Varsayımlar

**MM1.** Model paket tanım dosyasını bir sözlüğe indirger. Gerçek bir paket yöneticisinin hedef çözümlemesini, sürüm kısıtlarını ve platform koşullarını bu ders ölçmez; paket tanımı KAYNAK kalır.

**MM2.** "Derleme geçiyor" denetimi, bağımlılıkların çözülmesi ve döngü olmaması olarak modellenir. Gerçek derleme bundan fazlasını da kontrol eder (tip hataları, erişim denetimi); onlar bu modelin dışındadır.

**MM3.** Derleme süresi ölçülmez. Süre makineye, önbelleğe ve yüke bağlıdır; bu derste yerine bir iş birimi sayılır: değişen modülün yeniden derlenmesi gereken bağımlılarının sayısı.

## Sınırlayıcı Ölçüm

Aynı kuralın bir bedeli de yeniden derlemedir. Bir modül değişince onu doğrudan ya da dolaylı bağlanan her modül yeniden derlenir. Her değişen modül için bir bütçe koyarız: dersin kuralı olarak `profile` değişince en çok bir modül yeniden derlenmeli, `core` değişince en çok dört. Sayı süre değil, iş birimidir.

```swift
// main.swift — degisen modulun yeniden derlenecek bagimlilari
let good = shopGraph(faulty: false)
let bad = shopGraph(faulty: true)

let profileBudget = contrast("profile-butcesi", correct: good.dependents(of: "profile").count, faulty: bad.dependents(of: "profile").count) { $0 <= 1 }.describe()
let coreBudget = contrast("core-butcesi", correct: good.dependents(of: "core").count, faulty: bad.dependents(of: "core").count) { $0 <= 4 }.describe()

countSeparations([profileBudget, coreBudget])
```

```text
profile-butcesi | dogru=1/gecti kusurlu=2/kaldi -> ayiran
core-butcesi | dogru=4/gecti kusurlu=4/gecti -> kor
ayiran: 1
kor: 1
gurultulu: 0
ters: 0
```

Üç koşumda çıktı aynıdır. Doğru sürümde `profile` değişince yalnız `app` yeniden derlenir. Kusurlu sürümde `feed` de `profile`'a bağlandığı için `feed` ve `app` yeniden derlenir ve bütçe aşılır. Denetim ayıran olur. `core` ise iki sürümde de dört bağımlıya sahiptir: `feed`, `profile`, `settings` ve `app`. Kusur çekirdeğin bağımlı sayısını değiştirmez, bu yüzden aynı türden bir bütçe denetimi `core` için kördür. Aynı denetim bir modülde ayıran, ötekinde kör olabilir. Sınır ihlalinin bedeli yalnız onu içeren modülün çevresinde ödenir.

## Özet

- Modülerleştirme kodu paket sınırlarıyla böler; hangi modülün hangisine bağlanabileceği paket tanımında yazılır.
- Dersin kuralı: özellik modülleri yalnız çekirdek modüle bağlanır, birbirine bağlanmaz.
- Derlemenin geçmesine ve döngü olmamasına bakan denetimler sınır ihlaline karşı kördür; üç denetimin ikisi kör, biri ayıran çıktı.
- Kuralı uygulayan denetim, ihlali `feed->profile` olarak adıyla bulur ve ayıran olur.
- Değişen modülün yeniden derlenecek bağımlı sayısı iş birimidir ve bütçeye bağlanabilir; `profile` bütçesinde ayıran, `core` bütçesinde kör kalır.
- Bu dersin ayrımı: **kör**.

## Sonraki Adım

Mimari bölümü bitti: hesap ekrandan, gezinme görünümlerden, durum dağınıklıktan, bağımlılık içeriden ve özellikler birbirinden ayrıldı. Sıradaki konu testlerdir. İlk derste Birim Testleri, bu parçaların birini bir test hedefinde sınayacak. Orada kusur bir eşikte olacak: kargo ücreti, sepet tutarı eşiğin tam üstündeyken yanlış hesaplanacak, ve eşiğin altından bir değerle yazılmış test bu kusura karşı kör kalacak.
