↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)

5 dk okuma #flutter
Dizi · 59/61 Flutter Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
  3. Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
  4. Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
  5. Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
  6. Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
  7. Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
  8. Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
  9. Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
  10. Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
  11. Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
  12. Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
  13. Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
  14. Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
  15. Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
  16. Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
  17. Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
  18. Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
  19. Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
  20. Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
  21. Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
  22. Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
  23. Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
  24. Bölüm 6 — Konu 24: Provider Paketi
  25. Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
  26. Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
  27. Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
  28. Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
  29. Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
  30. Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
  31. Bölüm 7 — Konu 31: Deep Linking
  32. Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
  33. Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
  34. Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
  35. Bölüm 8 — Konu 35: Implicit Animasyonlar
  36. Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
  37. Bölüm 8 — Konu 37: Hero Animasyonları
  38. Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
  39. Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
  40. Bölüm 9 — Konu 40: Platform Channels
  41. Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
  42. Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
  43. Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
  44. Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
  45. Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
  46. Bölüm 10 — Konu 46: CI/CD
  47. Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
  48. Bölüm 10 — Konu 48: DevTools Profiling
  49. Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
  50. Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
  51. Bölüm 11 — Konu 51: Supabase Alternatifi
  52. Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
  53. Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
  54. Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
  55. Bölüm 12 — Konu 55: Flutter Web / Desktop
  56. Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
  57. Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
  58. Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
  59. Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
  60. Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
  61. Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
Dizinin sayfası →
İçindekiler 10 başlık
  1. Monorepo Nedir? Neden İhtiyaç Duyulur?
  2. Melos Nedir?
  3. melos.yaml — Monorepo'nun Tanımı
  4. Temel Komutlar
  5. Sürüm Yönetimi ve Toplu Yayınlama
  6. CI/CD ile Entegrasyon
  7. Ne Zaman Monorepo, Ne Zaman Polyrepo?
  8. Gerçek Dünya Örneği — Flutter'ın Kendi Ekosistemi
  9. 🎯 Bu Dersten Çıkarılması Gerekenler
  10. 📝 Ödevler

Bir önceki derste (Konu 58), kendi paketini/plugin'ini nasıl paketleyip yayınlayacağını öğrendik — hatta federated plugin mimarisinde birden fazla paketin (benim_pluginim, benim_pluginim_platform_interface, benim_pluginim_android...) bir arada geliştirildiğini gördük. Peki bu paketler ayrı ayrı Git repo'larında mı yaşamalı, yoksa tek bir repo'da bir arada mı? Bu ders, ikinci seçeneği — monorepo mimarisini — ve bunu yönetmenin standart Flutter aracı olan Melos'u işliyor.

Monorepo Nedir? Neden İhtiyaç Duyulur?

Monorepo ("mono" + "repository"), birden fazla bağımsız paketin/uygulamanın, tek bir Git repo'sunda bir arada tutulmasıdır — karşıtı, her paketin kendi ayrı repo'sunda yaşadığı "polyrepo" yaklaşımıdır.

metin
# Polyrepo (her paket ayrı repo):
github.com/sirket/benim_pluginim
github.com/sirket/benim_pluginim_platform_interface
github.com/sirket/benim_pluginim_android
github.com/sirket/benim_pluginim_ios

# Monorepo (tek repo, birden fazla paket):
github.com/sirket/benim_pluginim_monorepo/
├── packages/
│   ├── benim_pluginim/
│   ├── benim_pluginim_platform_interface/
│   ├── benim_pluginim_android/
│   └── benim_pluginim_ios/

Neden monorepo? Konu 58'deki federated mimariyi hatırla — dört ayrı paketi birlikte geliştiriyorsun. Polyrepo'da, platform_interface paketinde bir arayüz değişikliği yaptığında, bunu dört ayrı repo'da, dört ayrı git commit/git push ile senkronize etmen gerekir; bir PR, tek bir değişikliği gösteremez çünkü değişiklik birden fazla repo'ya yayılmıştır. Monorepo'da, tek bir commit, tüm paketlerdeki ilgili değişikliği birlikte taşır — bu, gerçek dünyadaki büyük Flutter projelerinde (hatta Flutter'ın kendi SDK'sı da kısmen bu mantıkla organize edilir) yaygın bir tercihtir.

Pratik senaryo — ne zaman işine yarar:

  • Bir şirket, birden fazla uygulaması (müşteri app'i, kurye app'i, admin paneli) arasında ortak bir ui_kit paketi ve ortak bir api_client paketi paylaşıyorsa.
  • Konu 58'deki gibi bir federated plugin geliştiriyorsan.
  • Bölüm 7 Konu 34'te öğrendiğimiz Clean Architecture'ı paket seviyesinde uygulamak istiyorsan — örneğin domain, data, presentation katmanlarını her birini ayrı bir Dart paketi yaparak (bu, katmanlar arası bağımlılığı pubspec.yaml'daki dependencies ile derleme zamanında zorunlu kılar — bir presentation paketi, yanlışlıkla data paketinin iç detaylarına erişemez, çünkü onu hiç import etmemiştir).

Melos Nedir?

Melos, monorepo'daki birden fazla Dart/Flutter paketini yönetmek için yazılmış bir komut satırı aracıdır — "birden fazla paket için flutter pub get'i tek seferde çalıştır", "değişen paketleri bul ve hepsini birden pub.dev'e yayınla" gibi işleri otomatikleştirir.

bash
dart pub global activate melos

melos.yaml — Monorepo'nun Tanımı

yaml
# melos.yaml (repo kök dizininde)
name: benim_pluginim_monorepo

packages:
  - packages/**   # bu desene uyan HER pubspec.yaml, Melos tarafından "paket" sayılır

scripts:
  analyze:
    run: melos exec -- flutter analyze     # Konu 46'daki flutter analyze'ı HER paket için çalıştırır
  test:
    run: melos exec -- flutter test        # Konu 45'teki testleri HER paket için çalıştırır

packages: - packages/** deseni, packages/ klasörü altındaki her pubspec.yaml içeren klasörü otomatik olarak bir workspace paketi sayar — yeni bir paket eklediğinde, melos.yaml'ı elle güncellemene gerek kalmaz.

Temel Komutlar

bash
melos bootstrap   # (kısaca: melos bs) — TÜM paketlerin bağımlılıklarını kurar ve
                   # birbirine bağımlı olanları YEREL sembolik link ile bağlar

melos exec -- flutter analyze   # verdiğin komutu HER paket içinde sırayla çalıştırır
melos run analyze               # melos.yaml'da tanımlı "analyze" script'ini çalıştırır

melos list       # workspace'teki tüm paketleri listeler
melos list --graph  # paketler arası bağımlılık grafiğini gösterir

melos bootstrap'ın kritik özelliği — yerel bağımlılık linki: Normalde, benim_pluginim paketi benim_pluginim_platform_interface'e bağımlıysa, pubspec.yaml'da şunu yazman gerekir:

yaml
dependencies:
  benim_pluginim_platform_interface: ^1.0.0   # pub.dev'den İNDİRİLMİŞ sürüm

Ama sen her ikisini de aynı anda geliştiriyorsan, platform_interface'te yaptığın bir değişikliğin, pub.dev'e yayınlamadan, hemen benim_pluginim'de görünmesini istersin. melos bootstrap, bunu otomatik olarak halleder — pubspec_overrides.yaml adlı bir dosya kendisi oluşturur ve paketi yerel klasördeki sürüme yönlendirir:

yaml
# melos'un OTOMATİK oluşturduğu pubspec_overrides.yaml (elle düzenlenmez, .gitignore'a eklenir)
dependency_overrides:
  benim_pluginim_platform_interface:
    path: ../benim_pluginim_platform_interface

Bu, Bölüm 7'de öğrendiğimiz Dependency Injection'a paralel bir fikir — geliştirme sırasında gerçek (pub.dev) bağımlılık yerine yerel (path-based) bir "sahte" bağımlılık enjekte ediyorsun, tıpkı testte gerçek bir Repository yerine FakeRepository enjekte etmen gibi.

Sürüm Yönetimi ve Toplu Yayınlama

Konu 53'te öğrendiğimiz semantic versioning, monorepo'da karmaşıklaşır — dört paketten sadece biri değiştiyse, sadece onu mu yeni sürüme çıkarmalısın, yoksa hepsini birden mi? Melos, bunu iki stratejiyle çözer:

bash
melos version   # DEĞİŞEN paketleri tespit eder, CHANGELOG.md'lerini
                # (Konu 58'i hatırla) OTOMATİK günceller, semantic versioning
                # kurallarına göre sürüm numaralarını artırır

melos publish --dry-run   # TÜM değişen paketleri, Konu 58'deki
                           # `flutter pub publish --dry-run` mantığıyla, TEK KOMUTLA dener
melos publish              # ve gerçekten yayınlar

melos version'ın en değerli özelliği: Conventional Commits formatında commit mesajları yazıyorsan (fix: ..., feat: ..., BREAKING CHANGE: ...), Melos hangi paketin hangi sürüm artışını (patch/minor/major — Konu 53) hak ettiğini otomatik hesaplar — bu, Konu 53'te elle karar verdiğimiz semantic versioning kararının, büyük ölçekte otomatikleştirilmiş halidir.

CI/CD ile Entegrasyon

Bölüm 10 Konu 46'da öğrendiğimiz GitHub Actions pipeline'ını hatırla — monorepo'da bunun tek bir farkı var: hangi paketin değiştiğini tespit edip, sadece onun testlerini/analizini çalıştırmak (gereksiz yere tüm paketleri test etmemek için):

yaml
# .github/workflows/ci.yml
- name: Melos bootstrap
  run: |
    dart pub global activate melos
    melos bootstrap

- name: Analyze & Test (tüm paketler)
  run: |
    melos run analyze
    melos run test

Küçük monorepo'larda (birkaç paket) genelde tüm paketleri her seferinde test etmek yeterince hızlıdır; büyük monorepo'larda (düzinelerce paket) melos exec --diff gibi bayraklarla sadece değişen paketleri hedeflemek mümkündür — bu, Konu 46'daki CI hızını optimize etme disiplininin monorepo'ya özel bir uzantısıdır.

Ne Zaman Monorepo, Ne Zaman Polyrepo?

Dürüst bir uyarı: Monorepo, her proje için gerekli değildir — tıpkı Konu 58'deki federated plugin mimarisinin "basit bir plugin için gereksiz" olması gibi:

  • Tek bir uygulama geliştiriyorsan (bu tutorial boyunca yaptığımız gibi), monorepo'ya ihtiyacın yok — lib/ klasörü altında modüler klasörleme (Bölüm 7'de öğrendiğimiz katmanlama) yeterlidir.
  • Birden fazla bağımsız paket/uygulama, aynı anda ve sıkça birbirine bağımlı olarak geliştiriliyorsa (bir şirketin birden fazla app'i + ortak kütüphaneleri, ya da Konu 58'deki gibi federated bir plugin ailesi), monorepo + Melos gerçek bir zaman kazancı sağlar.

Gerçek Dünya Örneği — Flutter'ın Kendi Ekosistemi

Bilmekte fayda var: Google'ın kendi flutter/packages reposu (kamera, google_maps_flutter, url_launcher gibi resmi plugin'lerin kaynak kodu), tam olarak bu monorepo mantığıyla organize edilir — onlarca federated plugin, tek bir repo'da, benzer bir yapı ve otomasyon (Melos'a benzer kendi araçlarıyla) ile yönetilir. Bu dersteki mimari, "teorik bir öneri" değil, Flutter ekosisteminin kendisinin kullandığı bir desendir.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Monorepo, birden fazla bağımsız paketi tek bir Git repo'sunda bir arada tutar; polyrepo'nun tersine, paketler arası senkronize değişiklikleri tek commit'te yönetmeyi sağlar.
  • Melos, monorepo'daki paketleri yönetmek için standart Flutter aracıdır — melos.yaml ile paketleri tanımlar, melos bootstrap/exec/run ile ortak komutları otomatikleştirir.
  • melos bootstrap, birbirine bağımlı paketleri pubspec_overrides.yaml ile yerel path'e bağlar — pub.dev'e yayınlamadan eşzamanlı geliştirmeyi mümkün kılar (Dependency Injection'a paralel bir fikir).
  • melos version/melos publish, Konu 53'teki semantic versioning ve Konu 58'deki pub publish işlemlerini birden fazla paket için toplu ve (Conventional Commits ile) otomatik hale getirir.
  • Monorepo, tek bir uygulama için gereksizdir; birden fazla, sık birbirine bağımlı paket/uygulama (özellikle Konu 58'deki federated plugin senaryosu) için değerlidir — Flutter'ın kendi resmi plugin reposu da bu mimariyi kullanır.

📝 Ödevler

  • [ ] melos paketini kur, Konu 58'de oluşturduğun paket(ler)i packages/ altına taşıyarak basit bir monorepo iskeleti kur, melos.yaml yaz.
  • [ ] melos bootstrap çalıştır, oluşan pubspec_overrides.yaml dosyasının içeriğini incele ve ne işe yaradığını kendi cümlelerinle açıkla.
  • [ ] melos.yaml'a, Bölüm 10 Konu 45/46'daki test ve analiz komutlarını çalıştıran bir scripts bölümü ekle, melos run ile çalıştır.
  • [ ] Kendi cümlelerinle, "pubspec_overrides.yaml'ın, Bölüm 7'de öğrendiğimiz Dependency Injection ile neden benzer bir fikri paylaştığını" açıkla.
  • [ ] Flutter'ın resmi flutter/packages GitHub reposuna göz at, kaç plugin'in bu tek repo altında toplandığını gözlemle.

Sıradaki konu: Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine