Flutter Modüler Mimari

Muzibu Mobile için Kurumsal Düzeyde Yapı

Flutter 3.x Melos Monorepo Clean Architecture

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/