Volver al inicio

Casos de uso en BLoC Flutter: refactorización de arquitectura

El artículo describe la refactorización de BLoC en Flutter extrayendo la lógica de negocio a casos de uso. Se proporcionan ejemplos de código antes y después, ventajas para pruebas y escalabilidad. El enfoque se basa en arquitectura limpia.

BLoC + casos de uso: cómo simplificar la arquitectura Flutter
Advertisement 728x90

Separar BLoC y casos de uso en Flutter: Arquitectura limpia en acción

El patrón BLoC de Flutter a menudo se convierte en un objeto dios: los manejadores de eventos mezclan llamadas a servicios, validaciones y emisiones de estados. Esto convierte el mantenimiento, las pruebas y la escalabilidad en una pesadilla. Mover la lógica de negocio a casos de uso restaura la separación adecuada de capas siguiendo los principios de arquitectura limpia: presentación (UI y BLoC), dominio (casos de uso, entidades) y datos (repositorios, servicios).

Un caso de uso implementa un escenario específico: orquesta servicios, maneja errores, aplica reglas de negocio y permanece agnóstico a la UI. El BLoC se enfoca puramente en el flujo de datos unidireccional: maneja eventos y emite estados.

BLoC clásico: Donde la lógica se enreda con la UI

Un BLoC típico llama directamente a servicios mediante inyección de dependencias, realizando efectos secundarios justo en los manejadores. Aquí un ejemplo simplificado de ItemsBloc:

Google AdInline article slot
class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
  final FetchItemsService _service;

  ItemsBloc(this._service) : super(Initial()) {
    on<FetchItemsEvent>(_onFetchItemsEvent);
  }

  Future<void> _onFetchItemsEvent(FetchItemsEvent event, Emitter<ItemsState> emit) async {
    emit(Loading());
    try {
      final items = await _service.fetchItems();
      emit(Loaded(items: items));
    } catch (error) {
      emit(Error(error: error));
    }
  }
}

Problemas: acoplamiento fuerte, código difícil de probar y violaciones del principio de responsabilidad única (SRP). El BLoC sabe demasiado sobre datos, red y lógica de negocio.

Refactorización: BLoC como gestor puro de estados

Tras la refactorización, el BLoC solo mapea eventos a estados, sin dependencias:

class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
  ItemsBloc() : super(Initial()) {
    on<SetLoading>(_onSetLoading);
    on<SetLoaded>(_onSetLoaded);
    on<SetError>(_onSetError);
  }

  Future<void> _onSetLoading(SetLoading event, Emitter<ItemsState> emit) async {
    emit(Loading());
  }

  Future<void> _onSetLoaded(SetLoaded event, Emitter<ItemsState> emit) async {
    emit(Loaded(items: event.items));
  }

  Future<void> _onSetError(SetError event, Emitter<ItemsState> emit) async {
    emit(Error(error: event.error));
  }
}

El caso de uso maneja la orquestación:

Google AdInline article slot
class FetchItemsUseCase {
  final ItemsBloc bloc;
  final FetchItemsService service;

  FetchItemsUseCase({required this.bloc, required this.service});

  Future<void> call() async {
    bloc.add(SetLoading());
    try {
      final items = await service.fetchItems();
      bloc.add(SetLoaded(items: items));
    } catch (error) {
      bloc.add(SetError(error: error.toString()));
    }
  }
}

El BLoC se reduce a 20 líneas y se vuelve completamente testable: simula eventos y verifica estados.

Beneficios de los casos de uso como orquestadores

Un caso de uso no es solo un proxy: combina datos de repositorios, aplica reglas, registra actividad y maneja sincronizaciones. Depende de abstracciones (interfaces), no de implementaciones concretas.

Ventajas clave:

Google AdInline article slot
  • Testabilidad: Simula servicios/repositorios para pruebas unitarias de casos de uso.
  • Escalabilidad: Agrega nuevos escenarios sin tocar el BLoC (principio Abierto/Cerrado).
  • Flexibilidad en DI: Inyecta servicios en casos de uso; el BLoC permanece independiente.
  • Legibilidad: Los escenarios de negocio están aislados en clases dedicadas.

En un proyecto real, esta refactorización redujo el tiempo de desarrollo un 30%, disminuyó errores y simplificó las pruebas.

Flujo de datos y compensaciones

UI → caso de uso → servicios/repositorios → eventos al BLoC → estado de UI. El caso de uso conoce el BLoC (un compromiso de arquitectura limpia por simplicidad), pero la UI está libre de llamadas bloc.add.

BLoC habilita flujo de datos unidireccional: comportamiento predecible y pruebas fáciles. Los casos de uso se prueban independientemente: simula respuestas y verifica llamadas.

Lecciones clave

  • Separación de capas: BLoC como gestor de estados, casos de uso para lógica de dominio.
  • SRP en acción: Cada clase tiene una sola responsabilidad.
  • Pruebas simplificadas: Funciones puras en BLoC, mocks en casos de uso.
  • Escalabilidad: Funciona con Cubit, Riverpod u otros gestores de estado.

Este enfoque encaja en cualquier proyecto Flutter donde el BLoC se está inflando. Mide métricas antes/después: tamaños de clases, cobertura de pruebas, tiempo de entrega de funciones.

— Editorial Team

Advertisement 728x90

Leer después