Найти в Дзене
Иван Соловьев

Как распилить монолит: сравнение микросервисного и композиционного подхода

Оглавление

В статье попробую понятно и с картинками сопоставить 2 подхода к типовой задаче "распила" монолитного приложения.

В качестве примера выбираю знакомую мне сферу - платежи.

Вот наш монолит, знакомьтесь.

Монолитная Платежка
Монолитная Платежка

Монолит умеет принимать запросы на проведение платежа от интернет-магазинов и в зависимости от параметров направлять эти запросы в один из банков.

Фактически такая платежка является посредником между продавцом и банком, но наш сервис не простой прокси-посредник, а скорее агрегатор и хранитель всех платежей для нескольких интернет магазинов

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

Чтобы разделить монолит, мы должны знать все значимые бизнес-функции системы:

1. Проведение операций (платежей нескольких типов, возвратов, выплат, переводов, с использованием различных платежных инструментов)

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

2. Личный-кабинет для клиента (интернет-магазина или другого агрегатора продавцов)

  • поиск операций
  • статистика операций
  • открытие терминала для проведения платежей в определенной валюте

3. Администрирование системы

  • создание нового клиента
  • управление доступными операциями клиента
  • создание правил роутинга от клиента к банку в зависимости от различных параметров
  • управление лимитами для клиента и/или от банка
  • ведение черных списков платежных инструментов, IP-адресов и прочего

4. Служебные функции

  • модуль наблюдения за работоспособностью системы
  • модуль для проведения нагрузочных тестов
  • логи системы для разбора инцидентов
  • отдельная база данных с подготовленными витринами для аналитики - источниками данных для BI-систем.

Начинаем распил монолита...

Микросервисный подход

Посмотрим как выглядит типовая микросервисная архитектура.

Микросервисная хрущевка
Микросервисная хрущевка

Определим особенности нашего подхода

  • Внедряем API Gateway - это единая точка входа для запросов от Front UI. Все преимущества такого подхода расписывать не стану, а отправлю изучить рекламную страничку istio
  • Используем подход один сервис - одна БД и решительно отвергаем подход использования одной БД для нескольких сервисов
  • Применяем концепцию API First и не чураемся синхронных REST запросов между сервисами - скорость возведения здания в приоритете, кто-то осудит такой экономичный подход, но ведь у нас хрущевка, а не дворец
  • Стремимся к прекрасному - для асинхронных межсервисных взаимодействий используем брокер сообщений, стремимся воплотить лучшие образцы событийно-ориентированной архитектуры, нам близка концепция один писатель - много читателей, поэтому в качестве брокера мы выбрали kafka
  • Наш ФРОНТ "на любителя", но у нас нет сил делить наш Front UI на красивые микрофронтенды, но мы знаем про паттерн Backends for Frontends и очень хотим сделать красиво удобно, но, кажется, легче все снести и строить заново (жильцы нашего здания не поймут, приходится признать, что "реновация" порой дешевле, чем капитальный ремонт).

Микросервисный подход для платежки

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

Получив запрос от клиента, сore transaction последовательно вызывает: antifraud, client config, routing, limiter, а результаты операции отправятся в kafka, чтобы их прочитали: dwh, notificator и statistics.

Используем паттерны:

  • OutBox - для снижения нагрузки разводим операции на запись и чтение базы данных,
  • Adapter - чтобы не изменять ядро для создания каждой новой интеграции.
Микросервисная платежка
Микросервисная платежка

Композиционный подход для платежки

Выделяем полноценные бизнес-функции в отдельные сервисы.

Применяем концепцию: центральный оркестратор использует сервисы-агенты.

  • Все взаимодействия с клиентом - clients contacts
  • Статистика по операциям для клиента - clients statistics
  • Внутреннее конфигурирование клиента - clients configs
  • Все проверки операция на мошенничество - antifraud
  • Функция исполнения операции в указанном терминале банка - payments.
  • Спрут сервис-оркестратор, который предоставляет API для клиента и содержит бизнес-логику исполнения запроса - Operation manager

Под капотом у оркестратора и его агентов может быть микросервисная архитектура.

Композиционная архитектура платежки
Композиционная архитектура платежки

Сравним распределение функций по сервисам

Что изменилось в композиционном подходе?

  • antifraud, statistics и все служебные функции не изменились,
  • routing, limiter и client config - объединились в единый сервис,
  • notificator - стал сервисом clients contacts, взяв на себя все взаимодействия общения с клиентом,
  • core transaction - распался на 2 части: вся оркестрация, то есть вспомогательные вызовы других сервисов, вся внутренняя логика проведения операций остается за operation manager, а все интеграции с банками, то есть внешняя логика на payments.
Распределение функций по сервисам
Распределение функций по сервисам

Как изменилось ядро системы?

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

Адаптеры
Адаптеры

Давайте подумаем, не нагрузить ли сервис payments функцией маршрутизации операций? То есть добавить routing - ведь функция определения конкретного терминала банка необходима именно для проведения платежей.

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

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

OutBox
OutBox

Какие сервисы расширились при композиционном подходе?

notificator - стал сервисом clients contacts, взяв на себя все взаимодействия общения с клиентом.

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

Резюмируем

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

Поэтому мы используем брокер событий для синхронизации данных и оркестратор для запросов во вспомогательные сервисы и исполнения логики бизнес-процесса.

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

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

Выделение бизнес-функций, которые можно переиспользовать и объединение микросервисов по контекстам - это порой единственная возможность навести порядок.

Для больших компаний:

ЕСЛИ в микросервисной архитектуре сложно решиться на независимые релизные циклы из-за пересечения зон ответственности команд, а еще сложнее отказаться от общего регрессионного тестирования на всю систему, ТО в композиционном подходе точно нужно решаться, делать сервисы реально независимыми со своими системами мониторинга, интеграционными авто-тестами, стандартизированными логами... в общем, чтобы сделать красиво придется как следует поработать))

 

Мораль сей басни такова:

Чем чаще я беру на прокат велосипед, тем больше хочу свой хороший!

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

Выход из этого ада или многократного дублирования функций в создании сервисов понятных (задокументированных) и надежных, которые приятно использовать - будто для тебя делали!

Звучит как тост, на этом и закончу!