Introduccion
Diseñar un sistema bancario en línea moderno es una tarea bastante complicada. Al mismo tiempo, una serie de tareas para desarrollar la parte cliente de la aplicación están asociadas con el proceso de procesamiento de una gran cantidad de datos provenientes casi simultáneamente de varias fuentes de información. Los datos del sistema de banca remota (RBS), los servicios de mensajería instantánea, diversos servicios de información deben recibirse y procesarse en tiempo real aquí y ahora. Para resolver problemas de este tipo, los métodos de programación reactiva se usan ampliamente en la actualidad.
El término "programación reactiva" en sentido amplio significa una organización de la aplicación, en la que la propagación de los cambios en el sistema se produce como resultado del procesamiento de los estados de los flujos de datos. Problemas importantes con este método son la simplicidad de presentar los flujos de información y la posibilidad de responder a los errores que ocurren durante el procesamiento asincrónico de los resultados de la presentación.
En un sentido estricto, la programación reactiva de la interfaz de usuario web puede significar el uso de herramientas de desarrollo estándar, como la biblioteca RxJs. Esta biblioteca proporciona una representación discreta de secuencias de datos utilizando el objeto Observable, que sirve como fuente de información que ingresa a la aplicación a ciertos intervalos.
Considere las características del uso de la biblioteca en el ejemplo del diseño de la interfaz web de un banco en línea para pequeñas empresas. Al desarrollar la interfaz de usuario, utilizamos la plataforma Google Angular 6 con la biblioteca incorporada RxJs versión 6.
Tareas de diseño de IU reactivas
Para el usuario, la mayoría de las operaciones en el Banco de Internet a menudo se reducen a tres etapas:
- seleccionar la operación necesaria de la lista, por ejemplo, pagar un préstamo o reponer una cuenta;
- completar parcialmente el formulario correspondiente (los detalles de pago se completan automáticamente con el nombre de la organización o el nombre del beneficiario ingresado por el usuario);
- confirmación automática de operaciones mediante mensajes SMS o firmas electrónicas.
Desde el punto de vista del desarrollador, la implementación de estas etapas incluye la solución de las siguientes tareas:
- verificar el estado del sistema RBS, asegurando la relevancia de los datos sobre las operaciones en la lista;
- procesamiento asincrónico de flujos de datos al completar un formulario, incluidos los datos ingresados por el usuario y recibidos de servicios de mensajes de información (nombre, TIN y BIC del banco, por ejemplo);
- validación del formulario completado;
- Guardado automático de datos en el formulario.
Comprobación del estado del sistema RB
El proceso de obtención de datos relevantes del sistema RB, por ejemplo, información sobre una línea de crédito o el estado de una orden de pago, incluye dos etapas:
- verificar el estado de disponibilidad de datos;
- recibiendo datos actualizados.
Para verificar el estado actual de los datos, se realizan solicitudes a la API del sistema con un cierto período de tiempo y hasta que se reciba una respuesta sobre la disponibilidad de los datos
Hay varias respuestas posibles para el sistema RB:
- {empty: true}: los datos aún no están listos;
- el cliente puede recibir datos actualizados;
{ empty: false
Como resultado, la obtención de datos relevantes se realiza en forma de:
const MIN_TIME = 2000; const MAX_TIME = 60000; const EXP_BASE = 1.4; request()
Analicemos paso a paso:
- Enviamos una solicitud. solicitud ()
- La respuesta va a expandirse. Expandir es una declaración RxJS que repite recursivamente el código dentro de su bloque para cada próxima notificación para el Observable interno y externo, hasta que la secuencia informa que se completó con éxito. Por lo tanto, para completar la secuencia, es necesario devolver dicho Observable para que no haya otro siguiente: VACÍO.
- Si la respuesta llegó {vacío: verdadero}, entonces hacemos una segunda solicitud después de un cierto retraso de tiempo (delayTime). Para no sobrecargar el servidor con solicitudes, aumentamos el intervalo de tiempo para el ping con cada nueva solicitud.
- Si durante la siguiente solicitud, algo más vino en respuesta, entonces dejamos de hacer ping (devolver VACÍO) y devolver el resultado de la última solicitud al suscriptor (último () operador).
- Después de recibir la respuesta, tomamos el resultado y lo procesamos. Un objeto del formulario se suscribirá:
{ empty: false
Formas reactivas
Considere la tarea de diseñar un formulario web reactivo de un documento de pago utilizando la biblioteca ReactiveForms del marco angular.
Las tres clases base de la biblioteca FormControl, FormGroup y FormArray le permiten usar una descripción declarativa de los campos del formulario, establecer los valores iniciales de los campos y también establecer reglas de validación para cada campo:
this.myForm = new FormGroup({ name: new FormControl('', Validators.required),
Para formularios con una gran cantidad de campos, es costumbre usar el servicio FormBuilder, que le permite crearlos usando un código más compacto
this.myForm = this.fb.group({ name: ['', Validators.required], surname: '' });
Después de crear el formulario en la plantilla de la página de orden de pago, es suficiente especificar un enlace al formulario myForm, así como los nombres de sus campos nombre y apellido
<form [formGroup]="myForm"> <label>Name: <input formControlName="name"> </label> <label>Surname: <input formControlName="surname"> </label> </form>
El diseño resultante le permite generar y rastrear cualquier flujo de información que pase a través de los campos del formulario como resultado de la entrada del usuario y en función de la lógica empresarial de la aplicación. Para hacer esto, solo suscríbase a los eventos generados por el observador asincrónico del formulario ValueChanges
this.myForm.valueChanges .subscribe(value => { …
Suponga que la lógica empresarial define los requisitos para completar automáticamente los detalles del destino del pago cuando el usuario ingresa el TIN del destinatario o el nombre de la organización. El código para procesar los datos ingresados por el usuario en el TIN / nombre de la organización se verá así:
this.payForm.valueChanges .pipe( mergeMap(value => this.getRequisites(value))
Validación
Los validadores vienen en dos formas:
Nos encontramos con validadores síncronos regularmente: estas son funciones que verifican los datos ingresados cuando se trabaja con el campo. En términos de formas reactivas:
"Un validador síncrono es una función que toma formas de control y devuelve un valor verdadero si hay un error y, de lo contrario, falso".
function customValidator(control) { return isInvalid(control.value) ? { code: "mistake", message: "smth wents wrong" } : null; }
Implementaremos un validador que verificará si el usuario indicó en el formulario una serie de documentos, si el pasaporte se indicó previamente como el tipo de documento de identificación:
function requredSeria(control) { const docType = control.parent.get("docType"); let error = null; if (docType && docType.value === "passport" && !control.value) { error = { code: "wrongSeria", message: " " } } return error; }
Aquí también nos referimos al formulario padre y al usarlo obtenemos el valor de otro campo. Fue posible devolver simplemente verdadero como un error, pero en este caso se decidió hacer lo contrario. Puede capturar estos mensajes de error en el campo de errores de un control o formulario. Si el campo tiene varios validadores, puede especificar exactamente cuál de los validadores no pudo mostrar el mensaje de error deseado o ajustar la validación de otros campos.
El validador se agregará al formulario de la siguiente manera:
this.documentForm = this.fb.group({ docType: ['', Validators.required], seria: ['', requredSeria], number: '' });
Fuera de la caja, varios validadores comúnmente encontrados también están disponibles. Todos ellos están representados por métodos estáticos de la clase Validators. También hay métodos para componer validadores.
La incorrección de un campo lleva inmediatamente a la invalidez de todo el formulario. Esto se puede usar cuando necesita desactivar un cierto botón Aceptar, si el formulario tiene al menos un campo no válido. Luego todo se reduce a verificar una condición "myform.invalid", que devolverá verdadero si el formulario no es válido.
El validador asíncrono tiene una diferencia: el tipo del valor de retorno. El valor verdadero o falso debe pasarse en una promesa o en un Observable.
Cada control o cada formulario tiene un estado (mySuperForm.status), que puede ser "VÁLIDO", "NO VÁLIDO", "DESHABILITADO". Dado que cuando se utilizan validadores asincrónicos, puede que no esté claro en qué estado se encuentra el formulario en este momento, existe un estado especial de "PENDIENTE". Gracias a esta condición (mySuperForm.status === “PENDING”), puede mostrar el precargador o realizar cualquier otro estilo de formulario.
Guardado automático
El desarrollo de software bancario (software) implica trabajar con varios documentos estándar. Por ejemplo, estos son formularios de solicitud o cuestionarios, que pueden consistir en docenas de campos obligatorios. Cuando se trabaja con documentos tan voluminosos, se requiere soporte de autoguardado para mayor comodidad del usuario, de modo que si pierde su conexión a Internet u otros problemas técnicos, los datos que el usuario ingresó anteriormente permanecen en la versión borrador del servidor.
Estos son los aspectos principales del procedimiento de autoguardado para la arquitectura cliente-servidor:
- Las solicitudes de guardado deben ser procesadas por el servidor en el orden en que se realizaron los cambios. Si envía inmediatamente una solicitud a cada cambio, no puede garantizar que una solicitud anterior no vendrá después y no sobrescribirá los nuevos cambios.
- No es necesario enviar una gran cantidad de solicitudes al servidor hasta que el usuario haya terminado de ingresar, es suficiente hacer esto cronometrando.
- Si se han realizado varios cambios con un retraso relativamente grande, y la solicitud de los primeros cambios aún no ha regresado, entonces no hay necesidad de enviar solicitudes para cada cambio posterior inmediatamente después de la devolución de la primera solicitud. Puede tomar solo lo último, para no enviar datos irrelevantes.
El primer caso puede tratarse fácilmente con el operador
concatMap . El segundo caso se resolverá sin problemas usando
debounceTime . La lógica del tercero se puede describir como:
const lastRequest$ = new BehaviorSubject(null);
Permanece en saveQueue $ para enviar una solicitud. Tenga en cuenta la presencia del operador
exaustMap en lugar de concatMap. Este operador es necesario para ignorar todas las notificaciones del Observable externo hasta que el interno haya completado su observación ("compilado"). Pero en nuestro caso, si durante la solicitud habrá una cola de nuevas notificaciones, debemos tomar la última y descartar el resto. exaustMap eliminará todo, incluido el último. Por lo tanto, guardamos la última notificación en BehaviorSubject y, en la suscripción, si la solicitud completada actual es diferente de la última, volveremos a lanzar la última solicitud a la cola.
También vale la pena señalar ignorar los errores durante las consultas, implementado utilizando la
instrucción catchError . Puede escribir un manejo de errores más complejo con una notificación para el usuario de que se produjo un error al guardar. Pero su esencia es que cuando ocurre un error en la secuencia, la secuencia no debe cerrarse, como es el caso con el error y las notificaciones completas.
Conclusión
El nivel actual de desarrollo de tecnologías de programación reactiva con la biblioteca RxJS le permite crear aplicaciones cliente completas para sistemas bancarios en línea sin costos de mano de obra adicionales para organizar la interacción con interfaces altamente cargadas de sistemas bancarios remotos.
El primer conocido con RxJS puede ahuyentar incluso a un desarrollador experimentado que se enfrenta a las "complejidades" de la biblioteca que implementan el patrón de diseño "Observador". Pero, tal vez superando estas dificultades, en el futuro, RxJS se convertirá en una herramienta indispensable para resolver los problemas de procesamiento asincrónico de flujos de datos heterogéneos en tiempo real.