Привет, меня зовут Дмитрий Ульянов, я разработчик-эксперт в Gems development. Сегодня я вместе с вами загляну под капот одной из самых объёмных интеграций этого года и поделюсь лайфхаками масштабирования решения по отправке документов.
Наша команда разработки ZT team является держателем домена ГИСОГД, и задачи, прямо или косвенно относящиеся к этой теме, ложатся именно на нас. В начале года по инициативе product owner’а Натальи Федоровой к нам в бэклог попала задача по обеспечению интеграции региональных ГИСОГД на базе нашей системы Geometa с внешними федеральными системами в рамках построения цифровой вертикали.
За плечами нашей команды немалый опыт работы с ГИСОГД, чего только стоила практика выстраивания взаимодействия с федеральной системой ГИСОГД РФ в конце прошлого года. На первый взгляд может показаться, что «поженить» два сервиса – это задача в пару строчек кода, но всё не так просто
Для нас эта работа началась с передачи двух видов сведений (кому интересно, это про документы «Разрешение на строительство» и «Разрешение на ввод») в ИСУП (Информационную систему управления проектами) и ИС ГСН (Информационная система государственного строительного надзора). Но впереди нас ждут аналогичные работы по сотне документов. Это значит, что нам предстоит под каждый новый документ включаться всей командой и писать код. Выглядит непродуктивно. Хорошо, что PO поддержала нас и разрешила найти low-code или no-code решение. И раз уж вы читаете это, значит, у нас это получилось. Но обо всём по порядку.
Раз уж нам всё равно предстояло отправлять документы в шину внешней системы (в нашем случае в Интеграционную шину), то ещё на старте работ было принято решение максимально облегчить жизнь пользователя и максимально автоматизировать процесс. Согласитесь, зачем просить человека делать какие-то действия, например, нажимать кнопку для отправки, если этого можно не делать. Например, при передаче сведений о «Разрешении на строительство», если документ сформирован и согласован, система сама его и отправит.
А если этот процесс автоматизирован, то можно создать целый сервис, обслуживающий настраиваемые бизнес-процессы по отправке документов в иные системы. Мы нашли решение, когда каждый из бизнес-процессов порождается определённым событием и последовательно идёт по шагам своего выполнения. Каждый этап или шаг необходимо сделать максимально компактным, почти атомарным. Если вдруг следующий шаг выполнить невозможно, то процесс необходимо повторить с последнего успешного шага, и только в случае критической ошибки начать его с самого начала.
Например, в случае нашей интеграции мы не стали создавать шаг «получи XML электронной формы документа из архива из согласования данного документа и отправь куда следует», а разбили его на семь маленьких шажочков:
- из документа получается ссылка на архив с согласованными файлами;
- по ссылке архив скачивается из файлового хранилища системы;
- из архива извлекается электронная форма документа в XML-формате;
- проверяется, что эта XML действительно электронная форма документа РС;
- из неё формируются сообщения в шину для ИСУП и ГСН соответственно;
- отправляется сообщение в ИСУП;
- отправляется сообщение в ГСН.
Такое решение позволяет достичь несколько преимуществ.
Во-первых, тот факт, что система обрабатывает данные последовательно и в случае ошибки ей не нужно перезапускать процесс целиком, а достаточно выполнить его малую часть, позволяет довести каждый бизнес-процесс до достижения результата за короткое время.
Во-вторых, сделать процесс разработки и unit-тестирования обработчиков для таких шагов достаточно простым, ведь в них почти нет никакой логики.
В-третьих, использовать логику в других бизнес-процессах, то есть чем меньше делает обработчик, тем он более универсален и применим где-то ещё.
В какой-то момент у нас набралось достаточное количество таких написанных вручную обработчиков бизнес-процессов. Нам предстояло накрутить на них движок и структуру баз данных, которые обслуживали бы ход бизнес-процессов и хранили их истории. Так мы смогли бы обеспечить возможность составления конфигурационных файлов бизнес-процессов, то есть из атомарных шагов, как из кирпичиков, выстраивать цепочки взаимодействия для обработки и отправки данных.
Как настроить переход от шага к шагу?
Здесь нам на помощь пришло функциональное программирование, в частности Result type. Сначала мы заставили систему возвращать результат «ОК», если шаг осуществлен успешно, а если что-то пошло не так, то «Fail». Чуть позже к стандартным «OK» и «Fail» мы прикрутили некий код того самого «хорошо» или «плохо». Таким образом, в зависимости от ответа и его кода в конфигурационном файле, мы настроили его маршрутизацию на следующий шаг, или в случае Fail, например, перенаправление на шаг с ожиданием или повторным выполнением одного из предыдущих шагов.
И, конечно же, мы не забыли про необходимость существования в нашем движке RetryPolicy. Понимаем, что всякое в жизни бывает. Например, если при отправке сообщения в шину у customer’a (заказчика, пользователя) разорвалось интернет-соединение, то наша система просто повторит отправку.
Обработчик для RetryPolicy мы сделали гибким и настраиваемым: позволили указывать число попыток, время между ними и даже параметры нарастания временного интервала между попытками, и даже указывать, куда переходить, если все попытки исчерпаны и надежды утрачены.
Заметим, что настраиваемость мы обеспечили не только для обработчика RetryPolicy, но и для других.
Как настроить обработчик так, чтобы его можно было по-разному использовать в разных процессах?
Для этого необходимо предусмотреть возможность задавать конфигурацию. Например, обработчику при отправке сообщения в шину можно указать адрес этой шины, авторизационные данные и ID принимающей системы. При настройке также важно указать обработчику, куда положить результаты работы шага и из каких результатов какого шага взять входные данные. Дополнительно можно предусмотреть и иные параметры в зависимости от требуемого функционала.
А что, если бизнес-процессы будут тяжелые? Что, если нужно обеспечить параллельность их выполнения, а не ждать очереди?
Наш сервис мы завернули в docker-образ, а конфигурационные файлы вынесли в тома (volume) наружу. Таким образом, если нам нужно обеспечить быструю работу каких-то тяжелых бизнес-процессов, мы можем решить это путём запуска разных процессов в независимо работающих docker-контейнерах. Да, это может быть ресурсоёмко в некоторых случаях, но это будет оправдано.
В итоге сервис получился очень гибким и удобно расширяемым. Достаточно просто написать код, недостающий нам для новых процессов обработчиков, пополнить их библиотеку, и можно конфигурировать новые задачи.
На этом можно было бы закончить статью, но на деле одними конфигами для бизнес-процессов всё не обошлось. Во время тестов мы заметили похожие настройки в разных процессах. Так родился ещё один способ повышения нашей продуктивности и масштабируемости процесса. Мы решили вынести возможность управления этими настройками через отдельный словарь. Это позволило свести к минимуму вероятность ошибок разработчиков. Например, сейчас исключена возможность ввода разных URL в двух схожих процессах.
Дальше нам предстояло научить систему формировать электронные формы документов. В нашем случае это не пара-тройка или даже десяток различных документов, это целая сотня.
Мы обеспечили возможность выгрузки из Geometa не только данных документа, но и всего дерева связанных с ним объектов в XML-представление. Более того, для удобства пользователя и сокращения времени обмена данными добавили возможность запроса отдельных атрибутов документов (полей) или их групп.
Используя полученную XML и богатые возможности XSLT-преобразований, мы смогли сделать no-code’овым и аспект формирования электронных форм документов. Сейчас наш движок умеет запрашивать из Geometa XML и посредством использования XSLT-файлов преобразовывать исходную XML в необходимый формат электронной формы документа. Когда потребуется добавить обработку новых документов, достаточно внести правки в конфигурационных файлах модуля и настроить новые XSLT.
В итоге можем сказать, что крайне довольны полученным результатом и самим реализованным движком. Планируем использовать его не только в задачах интеграции с системами цифровой вертикали, но и с другими внешними сервисами, потихоньку расширяя возможности сервиса за счёт новых обработчиков шагов бизнес-процессов.