iOS ve Android'de Clean Architecture: Ölçeklenebilir ve Test Edilebilir Katmanlı Mimari Rehberi
Büyüyen mobil projelerde karşılaşılan en büyük problem, iş mantığı (business logic), ağ çağrıları ve kullanıcı arayüzünün (UI) birbirine sıkı sıkıya bağlanmasıdır (tight coupling). Sonuç: Bir API kontratı değiştiğinde 15 farklı UI dosyasının kırılması, yazılamayan birim testler ve bakım maliyetinin katlanarak artması.
Bu makalede; iOS (Swift) ve çok platformlu (Flutter/Dart) projelerde üretim seviyesinde (production-ready) Clean Architecture prensiplerinin nasıl kurgulanacağını, katman sınırlarını ve bağımlılıkların tersine çevrilmesini (Dependency Inversion Principle) somut kod bloklarıyla inceliyoruz.
1. Clean Architecture Temel Diyagramı ve Katman Kuralları
Robert C. Martin'in (Uncle Bob) ortaya koyduğu mimarinin mobil ekosistemdeki altın kuralı şudur: İç katmanlar dış katmanları bilmez. Bağımlılık akışı her zaman dıştan içe doğrudur.
text
+-------------------------------------------------------------+
| PRESENTATION LAYER |
| [UI (Widgets/Views)] <---> [State Holders (Bloc/VM)] |
+-------------------------------------------------------------+
| (Kullanır)
v
+-------------------------------------------------------------+
| DOMAIN LAYER |
| [Use Cases / Interactors] <---> [Entities] |
| [Repository Contracts (Interfaces)] |
+-------------------------------------------------------------+
^ (İmplemente Eder)
|
+-------------------------------------------------------------+
| DATA LAYER |
| [Repository Impls] <---> [Data Sources (Remote/Local)] |
| [DTOs / Serialization / Mappers] |
+-------------------------------------------------------------+
- Domain Katmanı: Saf (pure) iş mantığı. Hiçbir UI çerçevesine (Flutter Material, UIKit, SwiftUI) veya harici kütüphaneye (Dio, Alamofire) bağımlı değildir.
- Data Katmanı: Verinin nereden geldiğiyle ilgilenir (REST API, GraphQL, SQLite, Hive, CoreData). Domain katmanının tanımladığı arayüzleri uygular.
- Presentation Katmanı: Kullanıcıya veriyi gösterir ve kullanıcı girdilerini yakalar (BLoC, Riverpod, MVVM).
2. Domain Katmanı: Saf İş Mantığı ve Kontratlar
Domain katmanı uygulamanın kalbidir. Bir e-ticaret uygulamasında sepetten ürün silme akışını ele alalım.
Entity Tanımı (Dart)
dart
// domain/entities/cart_item.dart
class CartItem {
final String id;
final String title;
final double price;
final int quantity;
const CartItem({
required this.id,
required this.title,
required this.price,
required this.quantity,
});
double get subtotal => price * quantity;
}
Repository Arayüzü (Contract)
dart
// domain/repositories/cart_repository.dart
abstract interface class CartRepository {
Future, CartFailure>> getCartItems();
Future> removeItem(String itemId);
}
Use Case / Interactor
Her kullanım senaryosu tek bir sorumluluğa (Single Responsibility Principle) sahip olmalıdır:
dart
// domain/usecases/remove_cart_item_usecase.dart
class RemoveCartItemUseCase {
final CartRepository _repository;
const RemoveCartItemUseCase(this._repository);
Future> call(String itemId) async {
if (itemId.isEmpty) {
return Result.failure(CartFailure.invalidId());
}
return await _repository.removeItem(itemId);
}
}
3. Data Katmanı: DTO, Mapper ve Veri Kaynakları
API'den gelen JSON verisi doğrudan Domain Entity'sine çevrilmemelidir. Bunun yerine Data Transfer Object (DTO) kullanılır ve bir Mapper aracılığıyla Entity'ye dönüştürülür.
dart
// data/models/cart_item_model.dart
class CartItemModel {
final String productId;
final String productName;
final int unitPriceCents;
final int count;
CartItemModel.fromJson(Map json)
: productId = json['product_id'] as String,
productName = json['product_name'] as String,
unitPriceCents = json['unit_price_cents'] as int,
count = json['count'] as int;
// Entity Dönüştürücü (Mapper)
CartItem toEntity() {
return CartItem(
id: productId,
title: productName,
price: unitPriceCents / 100.0,
quantity: count,
);
}
}
Repository İmplementasyonu
dart
// data/repositories/cart_repository_impl.dart
class CartRepositoryImpl implements CartRepository {
final CartRemoteDataSource _remoteDataSource;
CartRepositoryImpl(this._remoteDataSource);
@override
Future> removeItem(String itemId) async {
try {
await remoteDataSource.deleteItem(itemId);
return const Result.success(null);
} on NetworkException catch (e) {
return Result.failure(CartFailure.network(e.message));
} catch () {
return Result.failure(CartFailure.unexpected());
}
}
}
4. Bağımlılıkların Çözülmesi (Dependency Injection)
Katmanların birbirini doğrudan başlatmaması (instantiate etmemesi) gerekir. Servis bulucu veya DI container (Service Locator / Injector) yapısı kurarak mock'lama süreçlerini kolaylaştırıyoruz:
dart
// core/di/injection_container.dart
import 'package:get_it/get_it.dart';
final sl = GetIt.instance;
Future initDependencies() async {
// Data Sources
sl.registerLazySingleton(() => CartRemoteDataSourceImpl(sl()));
// Repositories
sl.registerLazySingleton(() => CartRepositoryImpl(sl()));
// Use Cases
sl.registerLazySingleton(() => RemoveCartItemUseCase(sl()));
// Presentation (BLoC / Cubit / ViewModel)
sl.registerFactory(() => CartBloc(removeCartItemUseCase: sl()));
}
5. Clean Architecture Kullanmanın CTO ve Mühendislik Kazanımları
- Paralel Çalışma Esnekliği: Backend ekibi API kontratını netleştirdiği an, mobil mimar Use Case ve Repository Interface'lerini yazar. UI ekibi ve Data ekibi birbirini beklemeden sahte (mock) verilerle çalışabilir.
- Platform ve Kütüphane Bağımsızlığı: UI tarafında Flutter'dan SwiftUI'a geçiş yapılsa ya da ağ katmanında Dio yerine Ktor Client tercih edilse dahi Domain mantığı %100 korunur.
-
Yüksek Test Kapsamı (Test Coverage): Use Case'ler UI veya Network emülatörlerine ihtiyaç duyulmadan saf
unit testortamında saniyeler içinde test edilir.
Sonuç ve İletişim
Clean Architecture, küçük projelerde başlangıç maliyeti gibi görünse de orta ve büyük ölçekli kurumsal mobil projelerde bakım süresini %60 oranında azaltır ve kod tabanınızı geleceğe hazırlar.
Kurumsal mobil projelerinizde doğru mimariyi inşa etmek, mevcut teknik borcu temizlemek veya 1 yıl bakım garantili anahtar teslim native / cross-platform mobil uygulama çözümleri için benimle iletişime geçebilirsiniz.
Nuh Mehmet Demirkol
Bilgisayar Mühendisi | Mobil Yazılım Mimarı & Danışman
📍 Ofis: Ankara, Türkiye
🌐 Web & Portföy: https://nmdemirkol.com
💬 WhatsApp Direkt İletişim: +90 507 823 68 11
Top comments (0)