En la transición a una nueva tecnología, perdemos las herramientas habituales para el desarrollo. En algunos casos, nos vemos obligados a soportar su ausencia debido a algunas limitaciones técnicas, pero si es posible, llevamos las herramientas con nosotros. Al desarrollar aplicaciones de Android, tomé como ejemplo la arquitectura limpia propuesta por Fernando Cejas. Al comprender los patrones de diseño utilizados en Flutter, decidí abandonar esta arquitectura en favor de BLoC. Me acostumbré rápidamente a esta plantilla, es muy similar a la MVVM con la que trabajé anteriormente, pero no quería aguantar un detalle. Al llamar a los métodos de repositorio, tuve que detectar excepciones, convertirlas en algún tipo y, según el tipo, crear el estado necesario. En mi opinión, esto abarrota el bloque y porté el tipo Either utilizado anteriormente en proyectos de Android basados en Fernando.
Cualquiera vino de lenguajes de programación funcionales. Proporciona el valor de uno de los tipos posibles:
- Izquierda (en caso de falla);
- Correcto (si tiene éxito).
Implementación básica de cualquiera La implementación es muy básica, inferior a las soluciones en otros idiomas, pero hace frente a su tarea. Utilizo este tipo como resultado de todos los métodos del repositorio, y el manejo de excepciones se transfiere a la capa de datos. Esto elimina la necesidad de construcciones try / catch, lo que hace que el código sea más legible.
Ejemplo de prueba / captura class ContactBloc { final ContactRepository contactRepository; ContactBloc(this.contactRepository); @override Stream<ContactState> mapEventToState(ContactEvent event) async* { if (event is GetContactEvent) { yield LoadContactState(); try { var contact = contactRepository.getById(event.id); yield ContactIsShowingState(contact); } on NetworkConnectionException catch (e) { yield NetworkExceptionState(e); } catch (e) { yield UnknownExceptionState(e); } } } } abstract class ContactRepository { Future<Contact>getById(int id); }
Ejemplo con cualquiera class ContactBloc { final ContactRepository contactRepository; ContactBloc(this.contactRepository); @override Stream<ContactState> mapEventToState(ContactEvent event) async* { if (event is GetContactEvent) { yield LoadContactState(); final either = contactRepository.getById(event.id); if (either.isRight) { final contact = either.right; yield ContactIsShowingState(contact); } else { final failure = either.left; if (failure is NetworkFailure) yield NetworkFailureState(failure); if (failure is UnknownFailure) yield UnknownFailureState(failure); } } } } abstract class ContactRepository { Future<Either<Failure, Contact>>getById(int id); }
Con respecto a la legibilidad, alguien podría discutir. Quizás alguien está acostumbrado a probar / atrapar y estará en lo correcto a su manera, en su mayor parte esto es saborizante. Una ventaja adicional es que nosotros mismos podemos definir la jerarquía de Fallas y regresar en el lado izquierdo. Hagamos una falla abstracta, que sea común para todas las funciones ServerFailure, NetworkFailure y algunas funciones específicas de ContactFailure para la función actual, con herederos. En el bloque, sabremos exactamente qué falla esperar.
La desventaja de implementar Failure on Dart es la falta de clases selladas como en kotlin, de lo contrario no habría ningún problema con el lanzamiento. El lenguaje es joven, se está desarrollando activamente y espero que llegue el momento y tengamos herramientas para escribir manejadores de manera más sucinta.
Puede que a alguien no le guste esta implementación, puede que no tenga sentido, pero solo quería presentarle la posibilidad de un enfoque funcional para el manejo de errores en Dart, aunque el uso no resultó ser tan elegante como en otros idiomas.
Recursos:
Código fuente