Особенности использования библиотеки RxJs в системе онлайн-банкинга

Введение

Проектирование современной системы онлайн-банкинга представляет собой довольно сложную задачу. При этом ряд задач разработки клиентской части приложения связан с процессом обработки большого количества данных, поступающих практически одновременно из нескольких источников информации. Данные от системы дистанционного банковского обслуживания (ДБО), служб мгновенных сообщений, различных информационных сервисов должны приниматься и обрабатываться в режиме реального времени здесь и сейчас. Для решения задач подобного рода сегодня широко применяются методы реактивного программирования.

Термин «Реактивное программирование» в широком смысле означает такую организацию работы приложения, при которой распространение изменений в системе происходит в результате обработки состояний потоков данных. Важные вопросы при этом методе — это простота представления потоков информации и возможность реакции на ошибки, возникающие в процессе асинхронной обработки результатов представления.

В узком смысле реактивное программирование веб UI может означать использование готовых инструментов разработчика, например, библиотеки RxJs. Данная библиотека обеспечивает дискретное представление последовательностей данных с использованием объекта Observable, который служит источником информации, поступающей в приложение через определенные промежутки времени.

Рассмотрим особенности использования библиотеки на примере проектирования веб-интерфейса онлайн-банка для малого бизнеса. При разработке UI нами была использована платформа Angular 6 компании Google со встроенной библиотекой RxJs версии 6.

Задачи проектирования реактивного UI

Для пользователя выполнение большинства операций в интернет-банке зачастую сводится к трем стадиям:

  • выбор необходимой операции из списка, например, погашение кредита или пополнение счета;
  • частичное заполнение соответствующей формы (реквизиты платежа заполняются автоматически по вводимому пользователем названию организации или фамилии получателя платежа);
  • автоматизированное подтверждение операции с использованием смс-сообщения или электронной подписи.

С позиций разработчика реализация перечисленных стадий включает решение следующих задач:

  • проверка состояния системы ДБО, обеспечивающая актуальность данных об операциях в списке;
  • асинхронная обработка потоков данных при заполнении формы, включая данные, вводимые пользователем и получаемые от сервисов информационных сообщений (наименование, ИНН и БИК банка, например);
  • валидация заполненной формы;
  • автоматическое сохранение данных в форме.

Проверка состояния системы ДБО

Процесс получения актуальных данных от системы ДБО, например, информации о кредитной линии или статусе платежного поручения, включает две стадии:

  • проверку статуса готовности данных;
  • получение обновленных данных.

Для проверки текущего состояния данных производят запросы к АПИ системы с определенным промежутком времени и до получения ответа о готовности данных

Возможно, несколько вариантов ответов системы ДБО:

  • { empty: true } — данные еще не готовы;
  • обновленные данные могут быть получены клиентом;

  • ошибка.

В результате получение актуальных данных производится в виде:

Разберем пошагово:

  1. Отправляем запрос. request()
  2. Ответ переходит в expand. Expand — это оператор RxJS, который рекурсивно повторяет код в рамках своего блока на каждое оповещение next для внутреннего и внешнего Observable, пока поток не сообщит о своем успешном завершении. Поэтому, чтобы завершить поток, нужно вернуть такой Observable, чтобы не было ни одного next — EMPTY.
  3. Если в ответ пришел {empty: true}, то делаем повторный запрос через определенное время delay(delayTime). Чтобы не перегружать сервер запросами, увеличиваем время интервала у пинга с каждым новым запросом.
  4. Если в ходе очередного запроса пришло что-то иное в ответ, то прекращаем пинговать (возвращаем EMPTY) и отдаем результат последнего запроса подписчику (оператор last()).
  5. После получения ответа берем результат и обрабатываем. В сабскрайб попадет объект вида:

Реактивные формы

Рассмотрим задачу проектирования реактивной веб-формы платежного документа с использованием библиотеки ReactiveForms из состава фреймворка Angular.

Три базовых класса библиотеки FormControl, FormGroup и FormArray позволяют использовать декларативное описание полей формы, задавать начальные значения полей, а также устанавливать валидационные правила для каждого поля:

Для форм с большим количеством полей принято использовать сервис FormBuilder, позволяющий создавать их с применением более компактного кода

После создания формы в шаблоне страницы платежного поручения достаточно указать ссылку на форму myForm, а также имена ее полей name и surname

Полученная конструкция позволяет формировать и отслеживать любые потоки информации, проходящей через поля формы как в результате ввода данных пользователем, так и на основе бизнес-логики приложения. Для этого достаточно подписаться на события, генерируемые асинхронным наблюдателем формы ValueChanges

Предположим, бизнес-логика определяет требования по автоматическому заполнению реквизитов адресата платежа при вводе пользователем ИНН получателя или наименования организации. Код обработки данных, вводимых пользователем в поля ИНН/наименование организации, будет иметь вид:

Валидация

Валидаторы бывают двух видов:

  • синхронные;
  • асинхронные.

С синхронными валидаторами сталкиваемся регулярно — это функции, которые проверяют введенные данные при работе с полем. В терминах реактивных форм:
«Синхронный валидатор — это функции, которые принимают control формы и возвращают truthy-значение, если есть ошибка и falsy в противном случае.»

Здесь также обращаемся к родительской форме и с помощью нее получаем значение другого поля. В качестве ошибки можно было вернуть просто true, но в данном случае было решено поступить иначе. Эти сообщения ошибок можно перехватить в поле errors элемента управления или формы. Если у поля несколько валидаторов, можно точно указать, какой из валидаторов не прошел, чтобы отобразить нужное сообщение об ошибке или скорректировать валидацию других полей.

Валидатор в форму будет добавлен следующим образом:

Из коробки также доступно несколько часто встречающихся валидаторов. Все они представлены статическими методами класса Validators. Также есть методы для композиции валидаторов.
Некорректность одного поля ведет сразу же к невалидности всей формы. Это можно использовать в случае, когда нужно деактивировать некую кнопку ОК, если в форме есть хотя бы одно невалидное поле. Тогда все сводится к проверке одного условия “myform.invalid”, которое вернет true, если форма невалидна.

У асинхронного валидатора есть одно отличие — тип возвращаемого значения. Значение truthy или falsy должно быть передано в промисе или в Observable.

У каждого контрола или у каждой формы есть статус (mySuperForm.status), который может быть “VALID”, “INVALID”, “DISABLED”. Поскольку при использовании асинхронных валидаторов может быть непонятно в каком состоянии в данный момент форма, есть особый статус “PENDING”. Благодаря этому условию (mySuperForm.status === “PENDING”) можно отобразить прелоадер или сделать любую иную стилизацию формы.

Автоматическое сохранение

Разработка банковского программного обеспечения (ПО) подразумевает работу с различными типовыми документами. Например, это формы-заявления или анкеты, которые могут состоять из десятков обязательных полей. При работе с такими объемными документами для дополнительного удобства пользователя требуется поддержка автосохранения, чтобы при потере соединения с интернетом или иных технических проблемах данные, которые пользователь вводил ранее, остались сохраненными на сервере в черновом варианте.

Приведем основные аспекты процедуры автосохранения для клиент-серверной архитектуры:

  1. Запросы на сохранение должны быть обработаны сервером в порядке, в котором производились изменения. Если на каждое изменение сразу посылать запрос, то нельзя гарантировать, что более ранний запрос не придет следом и не перезапишет новые изменения.
  2. Не нужно отправлять на сервер большое количество запросов, пока пользователь не закончил ввод, достаточно делать это по таймингу.
  3. Если было сделано несколько изменений с относительно большой задержкой, а запрос на первые изменения еще не вернулся, то нет необходимости посылать запросы на каждое последующее изменение сразу по возвращению первого запроса. Можно взять только последний, чтобы не отсылать неактуальные данные.

С первым кейсом можно с легкостью справиться с помощью оператора concatMap. Второй кейс без проблем решится с помощью debounceTime. Логику третьего можно описать в виде:

Осталось в saveQueue$ отправить запрос. Отметим присутствие оператора exaustMap вместо concatMap. Данный оператор необходим для игнорирования всех нотификаций внешнего Observable, пока внутренний не завершил свое наблюдение («закомплитился»). Но в нашем случае если во время запроса будет очередь новых нотификаций, мы должны взять последний, а остальные отбросить. exaustMap отбросит все, в том числе и последний. Поэтому сохраняем последнюю нотификацию в BehaviorSubject, а в подписке, в случае если текущий отработанный запрос отличается от последнего — кидаем последний запрос в очередь заново.

Также стоит отметить игнорирование ошибок в ходе запросов, реализованное с помощью оператора catchError. Можно написать более сложную обработку ошибок с выводом уведомлений для пользователя, что при сохранении произошла ошибка. Но его суть в том, что при возникновении ошибки в потоке, не должно произойти закрытие потока, как это происходит при оповещениях error и complete.

Заключение

Сегодняшний уровень развития технологий реактивного программирования с использованием библиотеки RxJS позволяют создавать полноценные клиентские приложения для систем онлайн-банкинга без дополнительных трудозатрат на организацию взаимодействия с высоконагруженными интерфейсами систем ДБО.

Первое знакомство с RxJS может отпугнуть даже опытного разработчика, столкнувшегося с “хитросплетениями” библиотеки, реализующими шаблон проектирования “Наблюдатель”. Но, возможно, преодолев эти трудности, в дальнейшем RxJS станет незаменимым инструментом при решении задач асинхронной обработки потоков разнородных данных в режиме реального времени.

FavoriteLoadingДобавить в избранное
Posted in Без рубрики

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *