Sparround

BLoC-un təkamülü və qarışıq codebase

BLoC-un API-sı illər üzrə əhəmiyyətli dəyişikliklər keçib. Köhnə koda düşdükdə bunları tanımaq lazımdır — rəsmi migrasiya bələdçisindən:

bloc v8.0.0:

  • mapEventToState silindi, yerinə on<Event> API-sı gəldi.
  • transformEvents silindi, yerinə EventTransformer API-sı.
  • TransitionFunction typedef-i silindi.
  • listen silindi, yerinə stream.listen.
  • Bağlanmış (closed) bloc-da emit çağırmaq artıq StateError atır və bu, tutulmamış exception kimi onError-a çatdırılır. Sənədin izahı: əvvəl belə hallarda heç nə baş vermirdi və nəyin səhv getdiyini anlamaq çətin idi.

bloc v9.0.0:

  • Bütün əvvəl deprecated edilmiş API-lar silindi; BlocOverrides Bloc.observerBloc.transformer xeyrinə aradan qaldırıldı.
  • Yeni EmittableStateStreamableSource interfeysi əlavə edildi: bloc_test əvvəl BlocBase-ə sıx bağlı idi, bu interfeys blocTest-i konkret implementasiyadan ayırmaq üçün gətirildi.

Ekosistemin digər paketləri öz versiya xəttini gedir: bloc_test 10.x, hydrated_bloc 11.x (orada HydratedCubit-in storage parametri adlı parametrə çevrildi).

dart
// ❌ Köhnə API (bloc v8-də silindi): generator funksiya ilə state yayımı.
class CounterBlocOld extends Bloc<CounterEvent, int> {
  CounterBlocOld() : super(0);

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    if (event is Increment) {
      yield state + 1;
    } else if (event is Decrement) {
      yield state - 1;
    }
  }
}

// ✅ Aktual API: hər event tipi üçün ayrı handler.
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<Increment>((event, emit) => emit(state + 1));
    on<Decrement>((event, emit) => emit(state - 1));
  }
}

// Nə qazanılır:
// - `if (event is ...)` zənciri aradan qalxır, tip yoxlaması handler-in imzasındadır
// - hər handler-ə ayrı EventTransformer vermək mümkün olur (debounce, droppable)
// - handler-lər ayrı metodlara çıxarılıb test edilə bilər

Köhnə API-nı tanımaq: `mapEventToState` → `on<Event>`.

Qarışıq codebase reallığı. Praktikada bir layihədə iki (bəzən üç) yanaşma yan-yana yaşayır: köhnə ekranlar Provider ilə, yeniləri BLoC və ya Riverpod ilə. Bu, texniki cəhətdən problem deyil — flutter_bloc özü package:provider üzərində qurulub — lakin idarə olunmalıdır.

İşləyən qaydalar:

  • Sərhədi sənədləşdir: hansı hissə hansı yanaşma ilə yazılır. Yazılı qayda olmadan hər PR-da mübahisə yaranır.
  • Feature daxilində qarışdırma: bir ekranda həm BlocBuilder, həm Consumer olması ən pis haldır — oxunaqlılıq və debug çətinləşir.
  • Yeni kod bir yanaşma ilə: "boy scout rule" — toxunulan köhnə kod tədricən köçürülür.
  • Ortaq layer-lər: repository/service qatı yanaşmadan asılı olmayan saxlanılır; bu, migrasiyanı ucuzlaşdıran əsas qərardır.
  • Onboarding sənədi: yeni developer-ə hansı ekranda nə gözlədiyini izah edən qısa mətn.
VəziyyətTövsiyə
Köhnə ekran işləyir, dəyişiklik planlanmırToxunmamaq — işləyən kodu köçürmək dəyər gətirmir
Köhnə ekranda yeni feature əlavə olunurHəmin ekranı köçürüb sonra əlavə etmək (əvvəlcə test yazmaqla)
Köhnə ekranda tez-tez bug çıxırKöçürmə üçün ən yaxşı namizəd — investisiya özünü doğruldur
İki yanaşma bir ekrandaTexniki borc kimi qeyd edib ən qısa müddətdə birləşdirmək
Repository qatı yanaşmaya bağlıdırƏvvəlcə bunu ayırmaq — migrasiyanın ən ucuz addımıdır

Müsahibədə qarışıq codebase sualına ən güclü cavab "hər şeyi köçürərdim" deyil, prioritetləşdirmə meyarıdır: köçürmə dəyəri bug tezliyi, dəyişiklik tezliyi və komandanın həmin sahədə işləmə ehtimalı ilə ölçülür. Toxunulmayan, sabit işləyən ekranı köçürmək — heç bir dəyər gətirməyən risk götürməkdir.

📚 Mənbələr və sənədlər