Привет, Хабр! Прежде чем погружаться в проблемы, давайте я расскажу, как у нас устроена микросервисная архитектура и куда мы идём. К сожалению или к счастью, в сегменте B2B в банковском и околобанковском обслуживании клиенты чаще пользуются веб-версиями приложений. Большие списки, зарплатные ведомости, работа с документами — всё это проще делать на большом экране. Постепенный переход в мобильную среду начинается только сейчас. 

Исходя из этого был построен скелет микросервисной архитектуры СберБизнес, с прицелом именно на специфику веб-разработки. В поддержании обратной совместимости в сравнении с мобильным приложением нет проблем, в эксплуатации могут одновременно находиться несколько версий, а фронтенд по всем канонам стараются делать тонким. На фронтенде минимальное количество логики, если сравнивать с мобильной версией, которая может иметь в себе базу данных и, конечно, свои модули. Они в какой-то степени напоминают микросервисы, ведь в крупных проектах без модуляризации никуда. Иначе разработчики будут выпивать по несколько чашек кофе, пока их инкремент собирается «на горячую». Я уже не говорю про «холодную». 

Микросервисы

Что из себя представляет наша архитектура? Она схематично представлена на рисунке 1. Давайте расскажу подробнее, что здесь есть и для чего.

Рисунок 1. Схема архитектуры СберБизнес.

Мобильное приложение устанавливает сетевое соединение с веб-сервером (NGINX) Mobile API Gateway, у которого есть две основные задачи:

  1. Предоставлять единую точку входа (один hostname) для мобильных приложений.

  2. Маршрутизировать запросы от «мобилы» на конкретное продуктовое приложение по контексту в слое Middleware.

Далее идёт слой Middleware — приложения без базы данных, но именно здесь понемногу появляется бизнес-логика. В этом слое реализуется канальная составляющая фронтального сценария на мобильном приложении: сбор данных из SCRUD-сервисов. По сути приложения в этом слое описывается программный конечный API для клиентской части (веб- и мобильного фронтенда). В этом слое есть как кросс-продуктовые сервисы — счета, подписание документов и другие, — так и сервисы исключительно продуктовые — кредитных, депозитных и других продуктов. 

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

Такая схема является отличной для веб-канала, где в слое Middleware каждая продуктовая команда описывает необходимый API для фронтенда, выстраивает интеграции с кросс-продуктовыми сервисами, и, в том числе, пишет фронтенд. Вся кодовая база для Middleware и фронтенда находятся в одном проекте репозитория. Максимально обособленная единица с точки зрения микросервисной архитектуры. Однако для мобильного приложения всё немножечко сложнее, ведь фронтенд лежит в другом репозитории — iOS/Android, и там есть свои взаимодействия. 

Архитектура мобильного приложения

Рассмотрим на примере Android, так как архитектура под iOS отличается не настолько сильно, чтобы говорить о ней отдельно. Принципиально существующая схема архитектуры мобильного приложения показана на рисунке 2.

Рисунок 2. Схема архитектуры мобильного приложения СберБизнес.

В мобильном приложении есть два типа модулей: продуктовые и кросс-продуктовые. На схеме к продуктовым относятся модули «Кредиты» и «Главная страница», а к кросс-продуктовым — «Счета» и «Подписание документов». Для работы функциональности «Кредиты» часть данных и механик получаем посредством локального взаимодействия с модулями «Счета» и «Подписание», и ещё часть — посредством сетевого запроса к бэкенду для получения специфичных данных для продукта «Кредиты»: специальных предложений, акций и т. д. В свою очередь, кросс-продуктовые модули также получают данные в момент загрузки главной страницы с бэкенда, но часть данных подтягивается по триггеру от клиента при его переходе в магазин приложений.

Почему мы считаем, что у нас есть проблема?

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

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

  1. «Единый источник правды». За получение счетов отвечает модуль «Счета» в мобильном приложении, а продуктовые модули взаимодействуют с его методами (см. рисунок 3).

  2. «Каждый получает сам». То есть для получения данных о счетах каждому модулю в мобильном приложении необходимо выполнить сетевой запрос в свой Middleware, который, в свою очередь, интеграционным методом получит данные о счёте из Middleware счетов. (см. рисунок 4).

Рисунок 3. Схема архитектуры «Единый источник правды».
Рисунок 4. Схема архитектуры «Каждый получает сам».

Какие проблемы мы видим в схеме «Единый источник правды»

В этой схеме мы видим проблемы разработки «узкого горлышка» — кросс-продуктового модуля Accounts. Ведь у разных бизнес-функциональностей есть свои потребности по работе со счетами: фильтрация по определённым признакам, добавление новых атрибутов к счетам и т. д. Всё это должно выполняться в рамках CR (change request) на команду, которая отвечает за разработку модуля счетов. То есть другим командам придётся становиться в очередь и ожидать выполнения доработки. Я бы назвал эту схему «проблема замедления T2M новой фичи». 

Но у схем есть и свои преимущества:

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

  2. В случае, если API кросс-продуктового модуля полностью удовлетворяет потребностям продуктовой функциональности, то со стороны продуктового модуля не требуется разработка логики, инкапсулированной в кросс-продуктовом модуле: получение списка счетов, его фильтрация, обновление данных по счёту и т. д.

  3. Выполняется один сетевой запрос, что, в конечном счёте, положительно влияет на энергоэффективность мобильного приложения. 

Какие проблемы мы видим в схеме «Каждый получает сам»

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

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

  2. Каждый модуль в мобильном приложении самостоятельно выбирает стратегию по работе с данными. Поэтому некоторые модули могут отображать неактуальные данные. В случае со счетами это будут неактуальные остатки.

К преимуществам схемы можно отнести нивелирование проблемы увеличения метрики T2M, так как в этом случае команда может ещё на уровне Middleware обогатить метод API теми данными, которые ей необходимы.

Что в итоге?

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

Антонина Иванова

руководитель направления, команда Mobile Core трайба «Цифровой корпоративный банк» Сбера