Комментарии 34
При этом архитектуру нужно было заложить с запасом прочности, в расчете на будущее расширение.
Как будто обычно архитектуру закладывают без запаса прочности и возможности расширения, а тут вот особенный кейс такой, что надо с запасом и чтобы приложение можно было расширять)
Видимо явное требование было, что не всегда бывает.
Ряд вопросов:
1. Как именно вы поддерживаете совместимость мобильного и веб клиентов с логикой бэка (совместимость старых версий через подмену статусов и логики из новых)?
2. Как идет разработка и поставка мобильного клиента и интеграции в бэке (отдельная ветка в Git для каждой версии клиента и описание интеграции в бэке под каждую версию или как-то иначе)? Сюда же неплохо было бы пояснение про стабильные релизы и вашу версионность — тема не раскрыта.
3. Как именно идет обработка счетов (так как счета нужны практически для любых продуктов банка)? Что за логика под Middle сервисом счетов?
4. Как у вас организовано получение данных клиента (так как данные клиента нужны для любых продуктов банка)? Все хранится в какой-то БД и подгруажется в кэш? Или внешняя система -> кэш? Шина -> кэш? Как обеспечивается отказоустойчивость?
5. В случае критической ошибки на минорной версии (например, берется не тот счет для операции, что не противоречит автотестам, но противорчит логике) — она у вас сразу деплоится с ошибкой? Не слишком ли это рискованно?
6. Как у вас организован процесс электронной подписи? Вы храните подпись где-то или клиент каждый раз для каждого сервиса должен подгружать ключ?
Сразу отметим, что базы данных для микросервисов изолированы друг от друга, что позволяет конфигурировать серверы баз данных для обеспечения требуемой доступности и скорости работы. Просим извинить, если по тексту сложилось другое впечатление.
1. Новый бэк пока используем только для нового мобильного приложения.
2. Мобильное приложение завязывается на определенный протокол взаимодействия с API сервера. На gateway указывается, какие версии сервисов соответствуют этому протоколу. Таким образом backend может релизить новые версии сервисов по их готовности, а когда мобилка, в свою очередь, готова с ними работать, мы выпускаем новый протокол.
3. Не совсем понятен вопрос, вы не могли бы конкретизировать?
4. Есть сервис, ответственный за данные клиента, который получает из АБС актуальные данные и хранит у себя в БД. При недоступности АБС данные могут браться из БД сервиса, если мы понимаем, что они актуальные (обновлялись недавно).
5. Есть несколько стендов, в том числе тест/предпрод. Такие ошибки выявляются на этапе тестирования.
6. К сожалению, не можем раскрыть эти подробности о проекте.
Уточняю по второму вопросу: как именно вы обходите конфликт версий? Если версия 1.4.123, к примеру, использовала адрес клиента одной строкой, а в новой версии 1.5.0 необходимо использовать две строки, то как вы это обходите? Делаете костыль для склеивания строк для старой версии (соответветственно, это идет адаптация под старые версии), дублируете данные (для старой и для новой версии), изначально предусматриваете в старой версии гибкость (дозагрузка атрибутов и ключей с бэка — в приложении просто абстрактная логика работы с тремя-четырьмя типами полей), предусматриваете более жесткие меры (отключение функционала для старых версий)?
2. Разные версии сервисов имеют разный API, соответственно МП работает со «своим» сервисом.
Здесь вопрос доработки таблиц базы данных для разных версий. В большинстве кейсов разные версии одного сервиса работают с одной таблицей. Есть кейсы, когда новый сервис будет использовать новые поля. Мы добавляем их в таблицу. Старый сервис продолжит только считывать данные и отправлять их пользователю. А новый сервис будет обновлять данные. Принцип немного похож на CQRS.
Вопросики:
1) Я один сломал мозг представив это? Кто, кого, когда обновляет… 3 абзаца абсолютно в разные направлениях. Просто прочитайте что вы написали:
KeyDB, Данные кэшируются не после запроса пользователя, а при изменении пользовательских данных, что позволяет иметь к ним доступ независимо от внутренних банковских систем.
API сервис счетов. Сервис сперва проверяет, есть ли актуальные данные пользователя в Cache. При успешном исходе возвращает данные, в ином случае отправляет запрос в Middle сервис счетов.
Например, сервис получает сообщение о входе пользователя в приложение и сразу же обновляет данные по счетам
2) По асинхронности, "сервис получает сообщение о входе пользователя в приложение и сразу же обновляет данные по счетам". Правильно я понимаю что всё асинхронно и допустим высокая нагрузка на систему, и я быстро зайду на страницу счетов, то могу увидеть старые данные? Считаете ли вы приемлемо такое в приложениях уровня финансы?
3) Вы используете кафку, так почему вы делаете столько промежуточных лишних звеньев(балансировщики, самодельные сервисы, кэши, сигналы для обновления кэшей), а не считаете в реалтайме в кафке?
Актуальный баланс можно сразу получать с помощью kafka\ktable в реальном времени. А изменения будут в виде отправить сообщений в топик balance {userId: 1, balance: +40},{userId: 1, balance: -40}
— Что касается №3, нам бы это решение не подошло, как минимум из-за единой точки отказа.
Как это отражается на необходимости и трудоемкости поддержки старых версий, апи, итп?
При этом архитектуру нужно было заложить с запасом прочности, в расчете на будущее расширение
И поэтому вы выбрали микросервисы? Вы не видите в этом противоречие? Микросервисы это изоляция по функционалу и по данным (каждый микросервис должен хранить данные в своей базе данных иначе считается что это не настоящие микросервисы). Ок, вы построили архитектуру и разбили по микросервисам — например платежами/переводами занимается один сервис а данными юзеров занимается второй сервис. Правильно? А потом на следующий день прилетает задача — вот мы хотим добавить программу лояльности и начислять юзеру какие-то баллы за переводы. И теперь для реализации этой задачи микросервису переводов нужно общаться с микросервисом который хранит данные юзеров. А теперь вопрос — как вы будете решать race-conditions и атомарное выполнение этой бизнес-логики? Речь не только про потерю связи, логику retry-ев на транспортном уровне (https://habr.com/ru/company/yandex/blog/442762) а про более фундаментальную проблему консистентной обработки данных и serializable уровня изоляции транзакций — https://www.youtube.com/watch?v=5ZjhNTM8XU8
В общем микросервисы можно применять только когда проект уже устоялся и не планирует расширяться, иначе добавление нового функционала имеет тенденцию увеличивать связность данных а это в свою очередь требует атомарного выполнения бизнес-логики которая обращается к разным микросервисам и реализации распределенных serializable-транзакций (иначе привет race-conditions и неконсистетность данных и дыры в безопасности)
Ок, вы построили архитектуру и разбили по микросервисам — например платежами/переводами занимается один сервис а данными юзеров занимается второй сервис. Правильно?
Нет, неправильно. Нет никакого «сервиса юзеров». разные поля данных, относящиеся к юзеру хранятся в разных сервисах.
прилетает задача — вот мы хотим добавить программу лояльности и начислять юзеру какие-то баллы за перевод
теперь для реализации этой задачи микросервису переводов нужно общаться с микросервисом который хранит данные юзеров
Нет, не нужно. Данные относящиеся к программе лояльности должны лежать в сервисе программы лояльности, даже если они относятся как 1 к 1 к user_id
В общем микросервисы можно применять только когда проект уже устоялся и не планирует расширяться, иначе добавление нового функционала имеет тенденцию увеличивать связность данных
Конечно же нет. Просто у вас в голове неправильный подход к их проектированию.
Как заметили выше, если микросервисы обращаются к единой БД, то это не кошерный микросервис, а скорее, просто веб сервис. В этом случае теряется смысл их разбивать, так как велико количество внутренних связей. Но это все вопросы терминологии.
Теперь было бы интересно услышать как изменилась стоимость владения и поддержки: расследование бага в микросервисной среде требует обычно в разы больше времени. Также сама философия разбиения на микросервисы предполагает создание множества отдельных БД, а значит кучу процессов синхронизации между ними, а значит кучу дополнительных тикетов в поддержку, которых в случае монолита бы не было.
В общем ждем статью "От монолита к микросервисам — месяц (или год) спустя"
Полностью согласен с комментариями выше о целесообразности использования микросервисов. Тоже ломали голову переходить или нет, в итоге не перешли). Оставили монолит, единственное думаем над тем, чтобы можно было разворачивать множество копий монолита на чтение для снижения нагрузки на один сервер и оставить один на изменение(консистентность данных в приоритете), разделив отправку запросов через какую нибудь проксю. Не было у кого-то подобного опыта?
История перехода от коробки к собственному продукту — да, она тут есть. И она вам помогла осуществить большую часть написанного. А микросервисы — разве что как аргумент моды.
Мы как-то делали монолит, который успешно выкатывался раз в день и ничего, работало
Это может быть не просто данью моды, а способом убедить начальство/заказчика вообще начать что-то делать. Манипуляция, наверное, такая.
Когда команд много, то сложно уследить за каждой. А в нашем случае было и вовсе невозможно. В такой ситуации микросервисы снижают риск написать некачественный код. Но даже если такое произойдет, то переписать 1 микросервис легче, чем распутывать цепочку связей в монолите.
Этот вопрос не был в полной мере освещен в статье, поскольку не относится к технике, больше к бизнесу.
Хотелось бы получить хоть какие-то ответы от автора на вопросы выше. А то все выглядит, как рекламная статья.
одно дело заплатить трудом, поддержкой и.т.д. а другое кажется они переделали на непозволительные юзкейсы для фин проекта.
Смотрите мой второй пункт (пока они его не прокоментировали):
https://habr.com/ru/company/simbirsoft/blog/512310/#comment_21883146
От монолита к микросервисам: ускорили банковские релизы в 15 раз