# Mobile Microservices Architecture: Modüler ve Ölçeklenebilir iOS
Büyük iOS projeleri zamanla monolit canavarlara dönüşür: derleme süreleri dakikalar alır, bir dosyadaki değişiklik beklenmedik yerleri kırar, ve yeni geliştiriciler projeye adapte olamaz. Microservices mimarisi backend dünyasını dönüştürdü — aynı prensipler mobil dünyada da devrim yaratabilir. Bu rehberde, iOS uygulamanızı bağımsız, test edilebilir ve ölçeklenebilir modüllere ayırmayı öğreneceksiniz.
Not: Mobile microservices, backend microservices ile birebir aynı değildir. Burada "micro" kelimesi, bağımsız feature modülleri anlamında kullanılmaktadır.
İçindekiler
- Monolit vs Modüler Mimari
- Module Boundaries Tasarımı
- SPM ile Module Yapısı
- Inter-Module Communication
- Dependency Injection Container
- Feature Module Anatomisi
- Build Performance
- Karşılaştırma Tablosu
- ALTIN İPUCU
- Sonuç ve Öneriler
1. Monolit vs Modüler Mimari
Karşılaştırma
Kriter | Monolit | Modüler | Micro-Feature |
|---|---|---|---|
**Derleme süresi** | Uzun (tümü) | Orta (değişen modül) | Kısa (tek modül) |
**Ekip ölçeği** | 1-5 geliştirici | 5-15 geliştirici | 15+ geliştirici |
**Code ownership** | Herkes her yer | Modül bazlı | Feature team |
**Test izolasyonu** | Zor | Kolay | Mükemmel |
**Bağımlılık yönetimi** | İmplicit | Explicit | Strict boundaries |
**Reusability** | Düşük | Orta | Yüksek |
**Onboarding** | Tüm projeyi anla | Modülü anla | Feature'ı anla |
**Merge conflict** | Sık | Nadir | Çok nadir |
**Release bağımsızlığı** | Yok | Kısıtlı | Feature flag ile |
2. Module Boundaries Tasarımı
Doğru sınırları belirlemek, modüler mimarinin en kritik adımıdır.
Katman Yapısı
swift
1// MARK: - Module Hierarchy2 3// TIER 1: Core Modules (sifir bagimlilik)4// - CoreFoundation: Extensions, utilities, protocols5// - CoreNetworking: HTTP client, request/response models6// - CoreStorage: Local storage abstractions7// - CoreUI: Design system, shared components8 9// TIER 2: Domain Modules (Core'a bagimli)10// - AuthDomain: Authentication models, protocols11// - PaymentDomain: Payment models, protocols12// - UserDomain: User models, protocols13 14// TIER 3: Feature Modules (Domain + Core'a bagimli)15// - AuthFeature: Login, register, forgot password UI16// - HomeFeature: Home screen, feed17// - ProfileFeature: User profile, settings18// - PaymentFeature: Checkout, payment history19 20// TIER 4: App Module (hepsini birlestirir)21// - MyApp: Composition root, dependency injection, navigationBağımlılık Kuralı
swift
1// Package.swift - Module bagimliliklari2// swift-tools-version: 5.93import PackageDescription4 5let package = Package(6 name: "AppModules",7 platforms: [.iOS(.v17)],8 products: [9 // Core10 .library(name: "CoreFoundation", targets: ["CoreFoundation"]),11 .library(name: "CoreNetworking", targets: ["CoreNetworking"]),12 .library(name: "CoreUI", targets: ["CoreUI"]),13 // Domain14 .library(name: "AuthDomain", targets: ["AuthDomain"]),15 .library(name: "UserDomain", targets: ["UserDomain"]),16 // Feature17 .library(name: "AuthFeature", targets: ["AuthFeature"]),18 .library(name: "HomeFeature", targets: ["HomeFeature"]),19 .library(name: "ProfileFeature", targets: ["ProfileFeature"]),20 ],21 targets: [22 // TIER 1: Core - sifir internal bagimlilik23 .target(name: "CoreFoundation"),24 .target(name: "CoreNetworking", dependencies: ["CoreFoundation"]),25 .target(name: "CoreUI", dependencies: ["CoreFoundation"]),26 27 // TIER 2: Domain - sadece Core'a bagimli28 .target(name: "AuthDomain", dependencies: ["CoreFoundation"]),29 .target(name: "UserDomain", dependencies: ["CoreFoundation"]),30 31 // TIER 3: Feature - Domain + Core'a bagimli32 .target(name: "AuthFeature", dependencies: [33 "AuthDomain", "CoreNetworking", "CoreUI"34 ]),35 .target(name: "HomeFeature", dependencies: [36 "UserDomain", "CoreNetworking", "CoreUI"37 ]),38 .target(name: "ProfileFeature", dependencies: [39 "UserDomain", "CoreNetworking", "CoreUI"40 ]),41 42 // Tests43 .testTarget(name: "AuthFeatureTests", dependencies: ["AuthFeature"]),44 .testTarget(name: "HomeFeatureTests", dependencies: ["HomeFeature"]),45 ]46)3. Inter-Module Communication
Feature modüller birbirini doğrudan import edemez. İletişim, protocol-based routing ile sağlanır.
swift
1// MARK: - Module Router Protocol (CoreFoundation'da)2protocol ModuleRouter {3 func route(to destination: RouteDestination) -> AnyView4}5 6enum RouteDestination: Hashable {7 case profile(userId: String)8 case settings9 case payment(productId: String)10 case auth(mode: AuthMode)11}12 13enum AuthMode: Hashable {14 case login15 case register16}17 18// MARK: - Feature Module Route Handler19// Her feature kendi route'larini handle eder20protocol FeatureRouteHandler {21 var handledRoutes: Set<String> { get }22 func handle(_ destination: RouteDestination) -> AnyView?23}24 25// ProfileFeature icinde:26struct ProfileRouteHandler: FeatureRouteHandler {27 var handledRoutes: Set<String> { ["profile", "settings"] }28 29 func handle(_ destination: RouteDestination) -> AnyView? {30 switch destination {31 case .profile(let userId):32 return AnyView(ProfileView(userId: userId))33 case .settings:34 return AnyView(SettingsView())35 default:36 return nil37 }38 }39}40 41// MARK: - Central Router (App Module'de)42final class AppRouter: ModuleRouter {43 private var handlers: [FeatureRouteHandler] = []44 45 func register(_ handler: FeatureRouteHandler) {46 handlers.append(handler)47 }48 49 func route(to destination: RouteDestination) -> AnyView {50 for handler in handlers {51 if let view = handler.handle(destination) {52 return view53 }54 }55 return AnyView(NotFoundView())56 }57}Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
4. Dependency Injection Container
swift
1// MARK: - DI Container2final class DIContainer {3 static let shared = DIContainer()4 5 private var factories: [String: () -> Any] = [:]6 private var singletons: [String: Any] = [:]7 private let lock = NSRecursiveLock()8 9 func register<T>(10 _ type: T.Type,11 scope: Scope = .transient,12 factory: @escaping() -> T13 ) {14 let key = String(describing: type)15 lock.lock()16 defer { lock.unlock() }17 18 switch scope {19 case .transient:20 factories[key] = factory21 case .singleton:22 factories[key] = { [weak self] in23 guard let self else { return factory() }24 if let existing = self.singletons[key] as? T {25 return existing26 }27 let instance = factory()28 self.singletons[key] = instance29 return instance30 }31 }32 }33 34 func resolve<T>(_ type: T.Type) -> T {35 let key = String(describing: type)36 lock.lock()37 defer { lock.unlock() }38 39 guard let factory = factories[key] else {40 fatalError("\(key) kayitli degil! register() ile kaydedin.")41 }42 guard let instance = factory() as? T else {43 fatalError("\(key) icin factory yanlis tip dondu")44 }45 return instance46 }47 48 enum Scope {49 case transient // Her resolve'da yeni instance50 case singleton // Tek instance51 }52}53 54// MARK: - Module Registration55// Her feature module kendi dependency'lerini kaydeder56protocol ModuleRegistrar {57 static func registerDependencies(in container: DIContainer)58}59 60// AuthFeature/AuthModule.swift61struct AuthModuleRegistrar: ModuleRegistrar {62 static func registerDependencies(in container: DIContainer) {63 container.register(AuthServiceProtocol.self, scope: .singleton) {64 AuthService(65 networkClient: container.resolve(NetworkClientProtocol.self),66 tokenStore: container.resolve(TokenStoreProtocol.self)67 )68 }69 container.register(LoginViewModel.self) {70 LoginViewModel(authService: container.resolve(AuthServiceProtocol.self))71 }72 }73}5. Feature Module Anatomisi
swift
1// MARK: - Ideal Feature Module Yapisi2//3// Sources/ProfileFeature/4// Public/5// ProfileFeatureAPI.swift (Public interface)6// ProfileRouteHandler.swift (Route handling)7// Internal/8// Views/9// ProfileView.swift10// EditProfileView.swift11// ViewModels/12// ProfileViewModel.swift13// Models/14// ProfileState.swift15// Services/16// ProfileService.swift17//18// Tests/ProfileFeatureTests/19// ProfileViewModelTests.swift20// ProfileServiceTests.swift21// Mocks/22// MockProfileService.swift23 24// Public API - sadece bu dosya public25public struct ProfileFeatureAPI {26 public static func makeProfileView(userId: String) -> some View {27 let vm = DIContainer.shared.resolve(ProfileViewModel.self)28 return ProfileView(viewModel: vm, userId: userId)29 }30 31 public static func routeHandler() -> FeatureRouteHandler {32 ProfileRouteHandler()33 }34}6. Build Performance
Modüler mimarinin en büyük avantajlarından biri derleme süresidir.
Build Süresi Karşılaştırması
Senaryo | Monolit | Modüler (SPM) | Kazanım |
|---|---|---|---|
**Clean build** | 180 sn | 160 sn | %11 |
**Incremental (1 dosya)** | 45 sn | 8 sn | %82 |
**Incremental (1 modül)** | 45 sn | 15 sn | %67 |
**Test (1 modül)** | 120 sn | 12 sn | %90 |
**CI full build** | 300 sn | 200 sn | %33 |
Build Optimizasyonları
swift
1// Xcode build settings per module2// SWIFT_COMPILATION_MODE = wholemodule (Release)3// BUILD_LIBRARY_FOR_DISTRIBUTION = YES (binary framework)4// EAGER_LINKING = YES (Xcode 15+)5 6// Pre-compiled binary cache7// CI'da her commit'te modulleri pre-build edin8// swift build --product CoreNetworking9// Artifact'i cache'leyin (GitHub Actions cache, Tuist warmup vb.)swift
1// MARK: - CI icin Dependency Graph Validator (Swift Script)2import Foundation3 4struct ModuleDependency: Codable {5 let name: String6 let tier: Int7 let dependencies: [String]8}9 10func validateDependencyGraph(_ modules: [ModuleDependency]) -> [String] {11 var violations: [String] = []12 13 for module in modules {14 for dep in module.dependencies {15 guard let depModule = modules.first(where: { $0.name == dep }) else {16 violations.append("HATA: '\(module.name)' bilinmeyen modulu import ediyor: '\(dep)'")17 continue18 }19 // Kural: Ust tier, alt veya esit tier'a bagimli olamaz20 if depModule.tier >= module.tier && module.tier > 1 {21 violations.append("IHLAL: Tier\(module.tier) '\(module.name)' -> Tier\(depModule.tier) '\(dep)'")22 }23 }24 }25 26 // Dongusel bagimlilik kontrolu (DFS)27 // ... topological sort ile cycle detection28 return violations29}30 31// CI'da: swift validate-deps.swift || exit 1ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:
Sonuç ve Öneriler
Mobile microservices mimarisi, büyük ekiplerin büyük uygulamalar geliştirmesi için vazgeçilmezdir. Doğru module boundary'leri çizin, bağımlılıkları strict tutun, inter-module communication'ı protocol-based yapın ve DI container ile composition root oluşturun. Derleme süreleri düşecek, test coverage artacak ve yeni geliştiriciler çok daha hızlı adapte olacak.

