Basit Anlatım (Herkes İçin)
Modüler yapı nedir? Uygulamayı LEGO parçaları gibi düşünün. Her parça (modül) bağımsız çalışır ve bir araya gelince tam uygulama oluşur. Bir parçayı değiştirirken diğerlerine dokunmazsınız.
Neden önemli? Laravel'de nWidart/Modules nasıl işleri düzenliyorsa, Flutter'da da benzer sistemler var. Karışıklığı önler, takım çalışmasını kolaylaştırır, test yazmayı basitleştirir.
Muzibu için öneri: Melos + Feature-First Clean Architecture
Bu kombinasyon hem Laravel'deki modül yapısına benzer, hem de Flutter dünyasında en yaygın kullanılan profesyonel yaklaşımdır.
Flutter Modüler Yapı Seçenekleri
| Yaklaşım | Laravel Karşılığı | Avantaj | Dezavantaj | Puan |
|---|---|---|---|---|
| Melos Monorepo | nWidart/Modules | Gerçek paket izolasyonu, versiyon yönetimi | Kurulum karmaşık | ⭐ 9.5/10 |
| Flutter Modular | Laravel Router + Modules | Kolay routing, lazy loading | Sadece routing odaklı | 7.5/10 |
| GetX Pattern | - | Hızlı geliştirme, az boilerplate | Anti-pattern riski, test zorluğu | 6/10 |
| Feature-First (Klasör) | Laravel Klasör Yapısı | Basit, hızlı başlangıç | Gerçek izolasyon yok | 7/10 |
Önerimiz: Melos + Clean Architecture + Riverpod
Müzik streaming gibi karmaşık uygulamalar için en uygun kombinasyon. Offline desteği, audio player, cache yönetimi gibi konularda net sınırlar çizer.
Melos Nedir? (nWidart/Modules Karşılığı)
Laravel'de nWidart/Modules:
- Her modül kendi klasöründe
- Kendi routes, views, controllers
- Kendi migrations, models
- Modül enable/disable
- Composer autoload
Flutter'da Melos:
- Her modül ayrı Dart package
- Kendi pubspec.yaml, testler
- Kendi models, repositories
- Bağımlılık yönetimi
- Workspace scripts (test, lint, build)
Melos Kurulumu:
# Global kurulum
dart pub global activate melos
# Proje root'unda
melos bootstrap # Tüm paketleri bağla
melos run test # Tüm paketlerde test
melos run analyze # Tüm paketlerde lint
Önerilen Klasör Yapısı
muzibu_mobile/ ├── melos.yaml # Melos workspace config ├── pubspec.yaml # Root pubspec (workspace) │ ├── apps/ │ └── muzibu_app/ # Ana uygulama (shell) │ ├── pubspec.yaml │ ├── lib/ │ │ ├── main.dart │ │ ├── app.dart │ │ ├── config/ # App config, themes │ │ ├── di/ # Dependency injection setup │ │ └── routing/ # GoRouter / AutoRoute │ └── test/ │ ├── packages/ # Shared packages (core) │ │ │ ├── core/ # Temel utilities │ │ ├── pubspec.yaml │ │ └── lib/ │ │ ├── constants/ # App sabitleri │ │ ├── extensions/ # Dart extensions │ │ ├── utils/ # Helper fonksiyonlar │ │ └── errors/ # Exception sınıfları │ │ │ ├── network/ # API katmanı │ │ ├── pubspec.yaml │ │ └── lib/ │ │ ├── api_client.dart # Dio wrapper │ │ ├── interceptors/ # Auth, logging, retry │ │ └── endpoints/ # API endpoint sabitleri │ │ │ ├── design_system/ # UI Kit │ │ ├── pubspec.yaml │ │ └── lib/ │ │ ├── atoms/ # Button, Text, Icon │ │ ├── molecules/ # Card, ListTile │ │ ├── organisms/ # SongCard, PlayerBar │ │ └── themes/ # ThemeData │ │ │ └── local_storage/ # Offline storage │ ├── pubspec.yaml │ └── lib/ │ ├── database/ # SQLite (drift/floor) │ ├── cache/ # Hive/SharedPrefs │ └── secure/ # flutter_secure_storage │ └── features/ # Feature modülleri │ ├── auth/ # Kimlik doğrulama │ ├── pubspec.yaml │ └── lib/ │ ├── data/ │ │ ├── datasources/ # Remote & Local │ │ ├── models/ # UserModel, TokenModel │ │ └── repositories/ # AuthRepositoryImpl │ ├── domain/ │ │ ├── entities/ # User entity │ │ ├── repositories/ # AuthRepository (abstract) │ │ └── usecases/ # Login, Logout, Register │ └── presentation/ │ ├── providers/ # Riverpod providers │ ├── screens/ # LoginScreen, RegisterScreen │ └── widgets/ # LoginForm, SocialButtons │ ├── player/ # Audio Player (EN KRİTİK!) │ ├── pubspec.yaml │ └── lib/ │ ├── data/ │ │ ├── datasources/ │ │ │ ├── audio_player_datasource.dart # just_audio │ │ │ └── hls_handler.dart # HLS ABR │ │ └── repositories/ │ ├── domain/ │ │ ├── entities/ # PlaybackState, Queue │ │ └── usecases/ # Play, Pause, Next, Seek │ └── presentation/ │ ├── providers/ # PlayerProvider (global) │ ├── screens/ # NowPlayingScreen │ └── widgets/ # MiniPlayer, Controls │ ├── library/ # Müzik kütüphanesi │ └── lib/ │ ├── data/ # Songs, Albums, Artists API │ ├── domain/ # Song, Album, Artist entities │ └── presentation/ # Browse screens │ ├── playlist/ # Playlist yönetimi │ └── lib/ │ ├── data/ │ ├── domain/ │ └── presentation/ │ ├── downloads/ # Offline indirmeler │ └── lib/ │ ├── data/ │ │ ├── download_manager.dart # İndirme kuyruğu │ │ └── encryption_service.dart # AES-256 │ ├── domain/ │ └── presentation/ │ ├── search/ # Arama │ └── lib/ │ └── settings/ # Ayarlar └── lib/
melos.yaml Yapılandırması
# melos.yaml - Workspace root'unda name: muzibu_mobile repository: https://github.com/muzibu/mobile packages: - apps/** - packages/** - features/** command: bootstrap: # Shared dependencies (tüm paketlerde ortak) environment: sdk: ">=3.0.0 <4.0.0" flutter: ">=3.16.0" dependencies: flutter_riverpod: ^2.4.0 freezed_annotation: ^2.4.0 json_annotation: ^4.8.0 dev_dependencies: build_runner: ^2.4.0 freezed: ^2.4.0 json_serializable: ^6.7.0 mocktail: ^1.0.0 scripts: # Tüm paketlerde test test: run: flutter test exec: concurrency: 4 packageFilters: dirExists: test # Tüm paketlerde analyze analyze: run: flutter analyze --fatal-infos exec: concurrency: 4 # Code generation (freezed, json_serializable) generate: run: dart run build_runner build --delete-conflicting-outputs exec: concurrency: 1 packageFilters: dependsOn: build_runner # Format kontrolü format: run: dart format --set-exit-if-changed . # Sadece features paketlerinde test test:features: run: flutter test packageFilters: scope: "features/*" # Clean all clean: run: flutter clean && rm -rf .dart_tool pubspec.lock
Clean Architecture Katmanları
Data Layer
- • Models (JSON → Object)
- • DataSources (API, DB)
- • Repository Implementations
Dış dünya ile iletişim
Domain Layer
- • Entities (saf iş nesneleri)
- • Repository Interfaces
- • UseCases (iş mantığı)
Framework bağımsız, test edilebilir
Presentation Layer
- • Screens (pages)
- • Widgets (UI components)
- • Providers/BLoC (state)
Kullanıcı arayüzü
Örnek: Auth Feature
// Domain: Entity (saf Dart sınıfı) class User { final String id; final String email; final String name; final SubscriptionType subscription; } // Domain: Repository Interface (abstract) abstract class AuthRepository { Future<Either<Failure, User>> login(String email, String password); Future<Either<Failure, User>> socialLogin(SocialProvider provider); Future<void> logout(); } // Domain: UseCase (tek iş mantığı) class LoginUseCase { final AuthRepository _repository; Future<Either<Failure, User>> call(LoginParams params) { return _repository.login(params.email, params.password); } } // Data: Model (JSON serialization) @JsonSerializable() class UserModel extends User { UserModel.fromJson(Map<String, dynamic> json); Map<String, dynamic> toJson(); } // Data: Repository Implementation class AuthRepositoryImpl implements AuthRepository { final AuthRemoteDataSource _remote; final AuthLocalDataSource _local; @override Future<Either<Failure, User>> login(...) async { try { final user = await _remote.login(...); await _local.cacheUser(user); return Right(user); } catch (e) { return Left(ServerFailure(e.message)); } } }
Paket Bağımlılık Akışı
┌─────────────────────────────────────┐
│ muzibu_app │
│ (Ana Uygulama Shell) │
└──────────────┬──────────────────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ auth │ │ player │ │ library │
│ (feature) │ │ (feature) │ │ (feature) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└──────────────────────┼──────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ network │ │ design_system │ │ local_storage │
│ (package) │ │ (package) │ │ (package) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└─────────────────────┼─────────────────────┘
│
▼
┌─────────────────────┐
│ core │
│ (package) │
└─────────────────────┘
YASAK Bağımlılıklar
- • Feature → Feature (doğrudan)
- • Package → Feature
- • Core → Herhangi bir üst katman
İZİN VERİLEN Bağımlılıklar
- • App → Features, Packages
- • Feature → Packages
- • Package → Core
State Management: Riverpod Önerisi
Neden Riverpod?
- Compile-time güvenlik
- Kolay test edilebilirlik
- Dependency injection entegre
- Code generation desteği
- Global state (player için ideal)
Muzibu İçin Provider Yapısı:
// Global providers (her yerden erişim) final playerProvider = StateNotifierProvider< PlayerNotifier, PlayerState>(...); final authProvider = StateNotifierProvider< AuthNotifier, AuthState>(...); final downloadQueueProvider = StateNotifierProvider< DownloadNotifier, DownloadState>(...); // Feature-scoped providers final playlistsProvider = FutureProvider.family< List<Playlist>, String>(...);
Player Modülü (En Kritik!)
15 saat kesintisiz çalışma gereksinimi! Memory leak, event listener birikimi, ve GC baskısı önlenmeli.
features/player/lib/ ├── data/ │ ├── datasources/ │ │ ├── audio_player_datasource.dart # just_audio wrapper │ │ ├── hls_handler.dart # HLS ABR logic │ │ ├── audio_session_handler.dart # Background play │ │ └── media_notification_handler.dart # Lock screen │ │ │ ├── models/ │ │ ├── playback_state_model.dart │ │ └── queue_model.dart │ │ │ └── repositories/ │ └── player_repository_impl.dart │ ├── domain/ │ ├── entities/ │ │ ├── playback_state.dart │ │ │ // isPlaying, isPaused, isBuffering │ │ │ // position, duration, bufferedPosition │ │ │ // volume, speed, repeatMode, shuffleMode │ │ │ │ │ ├── audio_quality.dart │ │ │ // ultralow(32k), low(64k), mid(128k), high(320k) │ │ │ │ │ └── playback_queue.dart │ │ // currentIndex, songs[], history[] │ │ │ ├── repositories/ │ │ └── player_repository.dart # abstract │ │ │ └── usecases/ │ ├── play_song.dart │ ├── pause.dart │ ├── resume.dart │ ├── seek.dart │ ├── next.dart │ ├── previous.dart │ ├── set_volume.dart │ ├── toggle_repeat.dart │ ├── toggle_shuffle.dart │ └── add_to_queue.dart │ └── presentation/ ├── providers/ │ ├── player_provider.dart # Global state │ ├── queue_provider.dart # Queue state │ └── audio_quality_provider.dart │ ├── screens/ │ ├── now_playing_screen.dart # Full screen player │ └── queue_screen.dart # Queue view │ └── widgets/ ├── mini_player.dart # Bottom bar player ├── player_controls.dart # Play/Pause/Next/Prev ├── seek_bar.dart # Progress slider ├── volume_slider.dart └── quality_selector.dart # HLS quality picker
Memory Leak Önleme Kuralları:
- • StreamSubscription → dispose()'da cancel()
- • Timer → dispose()'da cancel()
- • AnimationController → dispose()'da dispose()
- • History array → max 50 item, eski olanları sil
- • Image cache → LruCache ile limit
nWidart/Modules vs Melos Karşılaştırması
| Özellik | Laravel nWidart | Flutter Melos |
|---|---|---|
| Modül Oluşturma | php artisan module:make Blog |
Manuel veya mason template |
| Bağımlılık | composer.json (require) | pubspec.yaml (dependencies) |
| Enable/Disable | module:enable/disable |
packageFilters + ignore |
| Routes | Modules/X/routes/web.php | GoRouter / AutoRoute |
| Views | Modules/X/resources/views/ | features/x/presentation/screens/ |
| Models | Modules/X/Models/ | features/x/domain/entities/ |
| Migrations | Modules/X/database/migrations/ | packages/local_storage/database/ |
| Test | phpunit --filter Module |
melos run test |
Özet ve Sonraki Adımlar
Seçilen Stack:
- Melos - Monorepo yönetimi
- Clean Architecture - Katmanlı yapı
- Riverpod - State management
- GoRouter - Navigation
- Freezed - Immutable models
- just_audio - Audio player
Feature Modülleri:
- auth - Login, Register, Social
- player - Audio playback (kritik)
- library - Songs, Albums, Artists
- playlist - Playlist CRUD
- downloads - Offline
- search - Global search
- settings - Preferences
Başlangıç Komutları:
# 1. Proje yapısını oluştur
flutter create --org com.muzibu muzibu_mobile
cd muzibu_mobile
# 2. Melos kur
dart pub global activate melos
melos bootstrap
# 3. İlk feature'ı oluştur (mason ile)
mason make feature --name auth --output features/