Когда мы постепенно переводим легаси систему на новые рельсы, возникает множество случаев, когда само легаси и то, чем мы его заменяем должны взаимодействовать. Поскольку внесение изменений в легаси — это всегда долго, дорого и сложно, нам нужен механизм, который сможет интегрировать возможности новой системы, минимизируя при этом воздействие на легаси. Разбираемся, как можно использовать паттерн «Перехват Событий», чтобы пересылать их в новую систему.

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

Как это работает 

Внутри легаси, как правило, множество мест, события из которых можно перехватывать: брокеры сообщений, http API, SQL-соединения, файловые системы и прочее.

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

  • Сведение к минимуму изменений в легаси компонентах — как создающих, так и потребляющих события.

  • Возможность направлять события в новую систему — с дублированием или без него.

Инфраструктура обмена сообщениями

Если инфраструктура обмена сообщениями уже налажена, поздравляем — одно из ее ключевых преимуществ в возможности отделить производителя от потребителя сообщения.

Wire Tap, Message Router, Content-Based Router , например, можно использовать для перехвата и/или создания событий, направляемых и обрабатываемых новыми или существующими системами. Инфраструктура обмена сообщениями обычно позволяет использовать заголовки сообщений для фильтрации сообщений, делая определенные сообщения доступными для потребителей. Например, в реализациях JMS используются Message Selectors, позволяющие поставщикам сообщений фильтровать сообщения. Так, можно добиться простого перехвата и маршрутизации. 

На приведенном выше изображении у Legacy Consumer должен быть Producer, настроенный на включение Message Selector (для добавления фильтра).

Более сложная маршрутизация может потребовать проверки тела сообщения — в этом случае (Content Based Routing) сообщение должно быть получено компонентом маршрутизации, пэйлоад сообщения проверен, и сообщение повторно выставлено в новую очередь, согласно содержимому сообщения. 

Аналогично Legacy Consumer нужно будет перенастроить на получение сообщений из нового источника.

Обратный прокси — маршрутизация по URLам, Query Params или Form data

Через обратный прокси можем перенаправлять http-запросы на различные ресурсы. Аналогично инфраструктуре обмена сообщениями — обратный прокси может направлять запросы после простой проверки частей хоста и порта, путей, параметров запроса и заголовков. 

Например, маршрутизация запросов в зависимости от form data может потребовать индивидуальной реализации, но может обеспечить большую гибкость.

Шлюз API

Шлюз API обеспечивает уровень непрямой связи между эндпоинтом сервиса и его реализацией. API-шлюз дает нам возможность применить паттерн Branch by Abstraction, позволяя шлюзу направлять все запросы к выбранной нами реализации. На более тонком уровне можно также направлять запросы на обслуживание по мере необходимости в зависимости от запроса или содержимого пэйлоада.

Внутри веб-приложения — прогрессивное улучшение

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

И после: 

В качестве примера успешного применения этого подхода можно привести замену Product Images на витрине интернет-магазина. Мы создали новую систему, которая позволяла пользователям отправлять несколько изображений товаров, а специалистам — проверять их качество. Простое изменение внесли в глобальный шаблон старой витрины, чтобы добавить тег скрипта, Javascript содержимое которого можно было выпустить независимо. Скрипт использовал прогрессивное улучшение витрины, чтобы проверить, есть ли в новой системе изображения для продукта, и если есть, создать карусель для этих изображений. Отображение расширенной карусели с несколькими подробными изображениями позволило увеличить продажи.

Уровень базы данных — триггеры

Слишком поздно перехватывать события, когда они уже дошли до базы данных в легаси системе. Тем не менее, триггеры «Pre-commit» можно использовать для перехвата события записи в базу данных и выполнения различных действий. Например, строка может быть вставлена в отдельную таблицу Events для чтения/обработки новым компонентом, в то время как запись будет продолжаться как прежде (или прервется). Обратите внимание, что при изменении существующего поведения записи следует быть очень осторожным, так как вы можете нарушить важный неявный контракт.

Пример из практики: Инкрементное извлечение доменов

Одна из наших команд работала с клиентом, чья легаси система была нестабильной: ее стало сложно обслуживать, а обновления было слишком долго разрабатывать. Было решено на смену легаси коду реализовать возможности на основе Service Based Architecture.

Стратегия заключалась в использовании паттерна Strangler Fig и извлечении доменов, одного за другим, пока от исходного приложения не осталось практически ничего. При этом учитывались и другие моменты:

  • необходимость продолжать использовать легаси систему без перебоев

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

  • изменения в легаси приложении должны были быть сведены к минимуму — ощущалась острая нехватка знаний 

Состояние легаси

На диаграмме ниже показана архитектура унаследованной системы. Архитектура монолитной системы состояла в основном из слоев «представление-домен-данные».

Этап 1 — Скрытый запуск сервиса для одного домена

Сначала команда создала набор сервисов для одного бизнес-домена, а также возможность синхронизации данных, отображаемых этими сервисами, с легаси системой.

Сервисы использовали «скрытый запуск» — то есть не были доступны пользователям. Они позволяли команде проверить, что миграция и синхронизация данных достигли 100-процентного паритета с унаследованным хранилищем данных. Если возникали проблемы с проверкой согласованности, команда могла устранять их без ущерба для бизнеса.

​​

Перенос исторических данных сделали с помощью процесса миграции данных «одним выстрелом». Хотя это и не совсем перехват событий, постоянная синхронизация выполнялась с помощью Change Data Capture (CDC).

Этап 2 - Перехват всех операций чтения и перенаправление их на новый сервис

К этому моменту команда обновила Persistence Layer в легаси, чтобы перехватывать и перенаправлять все операции чтения (для этого домена) для получения данных из нового сервиса. Операции записи по-прежнему использовали хранилище данных из легаси системы. Это пример Branch by Abstraction — интерфейс Persistence Layer остается неизменным, а новая базовая реализация вводится в действие.

Этап 3 - Перехват всех записей и перенаправление на новую сервис

Операции записи (для домена) перехватывались и перенаправлялись на создание/обновление/удаление данных в новом сервисе.

Это изменение делало новый сервис системой записи для этих данных, поскольку старое хранилище данных больше не обновлялось. Любое последующее использование этих данных, например отчеты, также должно было быть перенесено, чтобы стать частью или использовать новый сервис.

Этап 4 - Перенос бизнес-логики домена в новый сервис

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

Когда использовать

Если вы используете паттерн Strangler Fig, вместе с ним нужно будет использовать и форму перехвата событий. При должном наличии творческого мышления можно найти много мест, в которых получится создать швы при помощи паттерна.

Некоторые альтернативные подходы

Человеческий «Перехват Событий»

Если предположить, что событие возникает в результате действий пользователя, то может появиться возможность перехватить это событие в самом начале, заставив пользователя повести себя иначе. 

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

Триггеры базы данных - пост-коммит

Триггеры «Post-commit» не перехватывают событие записи, но могут быть полезны для создания событий, уведомляющих другие системы о том, что произошло определенное изменение состояния.

Захват данных об изменениях (CDC)

CDC включает в себя технологии, позволяющие создавать поток событий из записей, добавляемых в журнал транзакций базы данных. Например, наши команды получили хороший опыт использования Debezium для создания потока событий Kafka, который может быть использован новыми приложениями. Опять же — не совсем перехват: поток событий может потребляться новой системой, работающей параллельно.

Маршрутизация событий в Legacy

Одна из целей перехвата событий — минимизировать или избежать необходимости обновления легаси системы для интеграции с новыми системами, но очевидной альтернативой является возможность просто модифицировать вызывающий модуль, чтобы он вызывал новую систему напрямую, а не внедрял перехватчик. Некоторые компромиссы, которые необходимо учитывать:

  • Разделение ответственности и требуемая сложность.

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

  • Избегание дополнительной сложности.

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

  • Если есть несколько систем вызывающих один API, а поставщик этого API заменяется, стоит рассмотреть возможность использования паттерна Legacy Mimic. Новый компонент будет реализовывать унаследованный интерфейс и, таким образом, возьмет на себя обязанности перехватчика событий без добавления дополнительной транзитной архитектуры.

Комментарии (0)