Привет! Эта статья написана командой системных аналитиков из ПСБ:
Юрием Моргуновым, Анастасией Самсонниковой, Ольгой Догадкиной, Максимом Белявским и Максимом Зуевым. Мы, как и многие компании, перешли с монолита на микросервисы. Причины тривиальны — их озвучивают, пожалуй, в каждой истории ухода от монолита: поддерживать всё это стало дорого, неудобно, монолитная архитектура замедляла процессы.
В общем, здесь начинается наша история. Если вы планируете переходить на микросервисы, эта статья для вас. Если же вы уже раздробили свой монолит, заглядывайте и дополняйте нас в комментариях.
В то же время в ПСБ проходило импортозамещение ряда программных продуктов. В частности, миграция с MS SQL на PostgreSQL, поэтому задача стала ещё интереснее.
Итак, микросервисы. Коротко пробежимся по их плюсам и минусам и пойдём к нашей задаче.

Плюсы:
Гибкое масштабирование. При монолитной архитектуре рост нагрузки требует вертикального масштабирования (усиление сервера), что быстро упирается в физические ограничения железа. А ещё нельзя точечно масштабировать проблемные компоненты. В условиях микросервисной архитектуры можно быстро развернуть новые экземпляры сервиса и снизить нагрузку.
Выше отказоустойчивость. Ошибка в одном сервисе не равна падению всей системы.
Гибкость технологий. Каждая команда выбирает стек под свои задачи с учётом своих условий и предпочтений.
Независимое развёртывание. Например, для добавления новой фичи достаточно отработать и пересобрать какой-то один микросервис, а не всё приложение целиком.
Недостатки у микросервисной архитектуры тоже есть:
Сложно правильно разделить: микросервис должен быть оптимального размера. Не слишком большим, не слишком маленьким. Дальше мы расскажем об этом поподробнее.
Трудозатраты на интеграции. Каждый новый сервис требует API, очереди сообщений, мониторинга.
Зависимость от других команд. Чужие данные = долгие согласования (а в монолите просто взяли бы из БД).
Проблемы владения: если сервис ничей, он быстро устаревает.
Нужны зрелые DevOps-процессы и чёткое проектирование.
Вот что мы хотели получить от микросервисов в первую очередь:
Ускорить поставку новой функциональности. Чтобы каждая команда смогла бы владеть своей функциональностью и своим кодом. А это значит, что изменения в методы вносятся быстрее, меньше времени уходит на ревью кода — и быстрее выходят доработки и новые фичи.
Дать возможность масштабировать отдельный функционал, чтобы мы могли при необходимости разворачивать дополнительные экземпляры сервиса.
Микросервисам быть! Что дальше?

Плохо спроектированные микросервисы создают те же проблемы, что и монолит, но с дополнительными накладными расходами. Здесь слово Ольге Догадкиной.
Успешность микросервисной архитектуры критически зависит от принципа «единой ответственности» (Single Responsibility Principle, SRP). Это означает, что у нас должно быть чёткое и однозначное понимание, какую единственную функцию или набор тесно связанных функций выполняет данный сервис. Микросервис должен иметь свою зону ответственности и отвечать за один точно определённый фрагмент бизнес-логики. Это не просто техническая группировка кода, а выделение конкретного бизнес-домена. Например, вместо монолита, управляющего всем, мы создаём отдельные сервисы: сервис управления заказами, сервис управления платежами, сервис уведомлений. Правильное определение зоны ответственности микросервиса и его границ делает его автономным, что впоследствии упрощает разработку, тестирование, поддержку и повышает гибкость всей системы.
Правильно спроектированный микросервис обладает максимальной внутренней связностью: внутри микросервиса находится необходимая бизнес-логика и наблюдается плотная связь между сущностями, изменения внутри не требуют правок в других сервисах. Такой микросервис не уронит вам всю функциональность, если в нём что-то засбоило, и не потребует вовлечения других команд.
Плохо спроектированный микросервис — это когда всё наоборот. Внутри себя микросервис содержит слабые логические связи, но плотно связан с другими микросервисами разными связями. Такая архитектура создаёт иллюзию декомпозиции, но по факту получается, что логика не находится внутри каждого отдельного микросервиса, а распределена между ними. При необходимости доработки такого микросервиса с большой долей вероятности потребуются доработки и в других микросервисах, с которыми он связан. А поскольку они могут находиться в зоне ответственности других команд, то, внося изменения в свой микросервис, мы автоматически добавляем в бэклог другой команды задачу, которую нужно согласовать, включить в спринт, ожидать реализации и далее синхронизировать релизы. Это замедлит выпуск в прод и, конечно, повысит time-to-market. Возникает вопрос: зачем было уходить от монолита?
Важно не только определить границы ответственности, но и контролировать размер микросервиса.
Если увлечься декомпозицией, можно создать так называемые наносервисы — сервисы, которые выполняют одну примитивную операцию и не могут выполнить значимую часть бизнес-процесса.
Наносервисы приводят к серьёзным архитектурным проблемам:
Экспоненциальный рост сетевого взаимодействия: вместо того чтобы выполнить операцию внутри одного процесса (как в монолите), для решения одной бизнес-задачи приходится делать десятки сетевых вызовов между «наносервисами». Это резко увеличивает задержки и нагрузку на сеть.
Усложнение CI/CD-конвейера: сборка, тестирование, развёртывание 100 крошечных репозиториев становится куда сложнее, чем управление 10 сервисами оптимального размера.
Сложность тестирования и мониторинга: отслеживание одного пользовательского запроса, проходящего через 15–20 «наносервисов», существенно замедляется.
Получение «распределённого монолита»: из-за огромного количества взаимозависимостей и необходимости синхронного развёртывания всех компонентов система теряет гибкость и автономию, ради которых всё затевалось.
При недостаточной декомпозиции микросервисов мы рискуем получить крупный микросервис — микромонолит, который теряет преимущества независимого деплоя и повторяет проблемы классического монолита.
Как понять, какой размер микросервиса оптимальный? Оптимальный размер сервиса — это баланс между независимостью команд и минимизацией сетевых вызовов, продиктованный границами конкретного бизнес-домена, а не техническими метриками. Сервис должен быть достаточно большим, чтобы решать конечную задачу автономно, и достаточно маленьким, чтобы оставаться гибким и управляемым одной командой примерной численностью 10–12 человек.
Определить границы микросервиса нам помогли следующие шаги, во многом основанные на принципах предметно-ориентированного проектирования (Domain-Driven Design, DDD):
Чётко определили цели и назначение микросервиса.
Проработали, как именно пользователи и другие системы будут взаимодействовать с сервисом, что позволило выявить необходимые функции и операции.
Спроектировали концептуальную модель данных для визуализации ключевых сущностей внутри контекста, чтобы убедиться в высокой внутренней связности данных.
Визуализировали схему связей с другими микросервисами, чтобы явно определить точки интеграции и типы связей.
Рассмотрели показатели прогнозируемой нагрузки на микросервис.
Выявили перспективы развития продукта на определённый промежуток времени в будущем, чтобы понимать, какие новыми функциями будет обрастать наш продукт.
Обратились к стратегии развития архитектуры. Тут важно помнить, что системные аналитики и архитекторы мыслят по-разному: архитектор смотрит на картину в общем и проектирует структуру в целом по системе, а аналитик намного больше погружается в детали бизнес-процессов и требований, проектирует таблицы, методы и схемы. Нужно балансировать разные уровни мышления.
Пилить монолиты не так-то просто… Первые грабли

Переход от монолитной архитектуры к микросервисной на бумаге выглядит просто, но на практике таит в себе множество подводных камней. Когда мы только начали процесс «дробления» нашего монолита, мы практически сразу столкнулись с рядом типичных, но критичных проблем. Расскажем, как мы всё это решили.
Первое — когда потребность создания микросервиса возникла сразу в нескольких командах, мы столкнулись со множеством вопросов. Как инициализировать новый сервис? Каков процесс выделения и настройки отдельной базы данных на тестовых стендах? Куда идти за техническими учётными записями и правами доступа? Как стандартизировать создание репозитория и оформить задачу в трекере? Чтобы оптимизировать процесс, минимизировать дублирование усилий и обеспечить единый подход, мы разработали два ключевых артефакта:
Пошаговая инструкция по запуску микросервисов. В этом документе мы детально прописали роли, ответственных лиц и конкретные действия для каждого этапа инициализации сервиса.
Шаблон документации микросервиса, содержащий всю критически важную информацию для описания микросервиса: название и предназначение сервиса, реквизиты БД и ссылки на репозитарии, ссылки на API и схемы зависимостей и другие полезные вещи.
Эти инструменты позволили нам формализовать процесс и масштабировать подход к разработке микросервисов.
Второе — столкнулись с проблемами «частичного отказа», когда фронтенд отправляет запросы к нескольким микросервисам для загрузки одной страницы, но ответ от бэкенда приходит с ошибкой. Тут мы продумали логику обработки ошибок и отображения информации на UI: в данном случае можно не отображать блок, по которому вернулся ответ с ошибкой, или отображать сообщение. На бэкенде можно использовать паттерны отказоустойчивости, такие как Circuit breaker (автоматический выключатель), чтобы предотвратить каскадные сбои. Также добавление мониторинга и логирования при разработке микросервисов послужило хорошей практикой, поскольку оно позволило отслеживать работу каждого сервиса, выявлять проблемы и оперативно на них реагировать. Настроенные алерты оперативно сообщали ответственным лицам о нездоровье какого-либо микросервиса и о других критических событиях и сбоях.
Третье — зависимость циклов развёртывания: часть обновлений требовалось выполнить в монолите, часть — в микросервисе, а обновления происходят в разное время. Мы передали контроль обновления микросервиса в руки DevOps-команды. Это позволяет нам деплоить сервис независимо от расписания релизов монолита. Также сейчас идём к минимизации внешних технических зависимостей (общие библиотеки, пакеты), чтобы наш микросервис работал и обновлялся изолированно.
В создании микросервисов участвуют специалисты нескольких ключевых направлений. Для успешного запуска требуется слаженная работа аналитиков, разработчиков, тестировщиков, системных администраторов, администраторов БД, DevOps-инженеров. На схеме показаны направления, задействованные в процессе создания микросервиса:

Про шаблоны хочется сказать побольше. Шаблоны — это прекрасно, они очень помогают экономить время и силы. На этапе инициализации сервисов возникает много повторяющейся рутинной работы. Запуск каждого микросервиса «с чистого листа» — это долгий и ресурсоёмкий процесс, который, по сути, представляет собой копирование и адаптацию одних и тех же компонентов из проекта в проект.
Мы разработали универсальный шаблон проекта для новых микросервисов, который обеспечивает быструю готовность решения к работе.
Процесс создания шаблона включал следующие этапы:
Подготовка пустой заготовки с базовой структурой.
Интеграция необходимых библиотек для управления конфигурациями и версионированием, генерации спецификаций (например, на базе OpenAPI/Swagger).
Добавление базового API‑функционала, включая аутентификацию, работу с сертификатами и электронными подписями.
Добавление базовых консьюмеров для взаимодействия с очередями сообщений.
Подключение обязательных компонент логирования и мониторинга.
Настройка базового CI/CD‑конвейера и определение стандартной архитектуры решения.
В результате мы не только сокращаем время на подготовку, но и обеспечиваем единообразие в разработке и снижаем вероятность ошибок, связанных с человеческим фактором.
И тут поджидает импортозамещение…
Здесь слово Максиму Зуеву, системному аналитику из Банковского сопровождения контрактов. Он тоже занимался выделением продукта в микросервис. И в то же время столкнулся ещё и с импортозамещением — и у нас, и у коллег-смежников.
Так сложилось, что параллельно с выделением в микросервис система меняла и СУБД: мы переходили с Microsoft SQL Server на PostgreSQL. И в первое время команде пришлось адаптироваться не только к особенностям микросервисов, но и к отличиям новой СУБД. Какие вопросы нужно было решить? Например:
Перенос данных. В технологическое окно, которое даётся на установку релиза, не всегда можно успеть перенести все данные, если их много. Мы нашли такой выход: некоторые данные перенесли до перевода работы микросервиса с новой базой данных. В момент установки релиза на них уже не пришлось тратить время.
Типизация данных. PostgreSQL и Microsoft SQL Server по-разному работают с некоторыми типами данных, регистром и функциями. Например, в PostgreSQL нет точных аналогов DATETIME2 или NVARCHAR(max), а имена объектов чувствительны к регистру, если не заключены в кавычки. Для дат и времени используются TIMESTAMP и функции вроде NOW() вместо GETDATE(). Синтаксис DDL тоже отличается: IDENTITY в SQL Server заменяется на SERIAL или GENERATED ALWAYS AS IDENTITY в PostgreSQL. При миграции или кроссплатформенной разработке это требует корректировки кода, особенно в запросах с TOP (SQL Server) и LIMIT/OFFSET (PostgreSQL).
Таблицы историчности. В Microsoft SQL Server истории изменений данных хранятся в таблицах по умолчанию, а в PostgreSQL их просто нет. Ну что же, мы сделали их сами: написали с помощью триггеров и получили тот же функционал.
Производительность. Переписали и оптимизировали средний слой запросов в PostgreSQL, чтобы запросы обрабатывались максимально быстро.
Материализованные представления. Вообще здесь есть разные варианты решения: переписать код или сделать табличное представление. Мы в целом пришли к решению от них отказаться.
Доступ к данным. В отличие от монолита, где все данные доступны в едином хранилище, микросервисы требуют сложного взаимодействия для получения информации из разных источников. Это создаёт дополнительные задержки и сложности при выполнении запросов. Впрочем, можно держать в своей базе и локальные копии необходимых данных: так они будут в быстром доступе.
Это ещё не всё. В тот период нам нужно было найти решение по интеграциям с ESB и ETL. С ESB при переходе на микросервисы всё оказалось довольно просто: нужно было лишь переписать end-point-ы, и ESB стала просто получать данные из нового места. Но нам было недостаточно такого решения. Всё-таки мы работаем со смежниками, а значит, нужно быть всегда готовыми к тому, что у них что-то может поменяться, а значит, нам нужно оперативно переработать решение.
Мы предусмотрели обратную совместимость: поддерживали как новые end-point-ы в микросервисах, так и старые — в монолите. В этот переходный период гибридной архитектуры шина могла забирать данные из любого места. Позже, когда мы уже были готовы к переключению на микросервис, старые end-point-ы удалили.
В случае с ETL нам нужно было не только развернуть таблицы в новой СУБД и предоставить туда доступ ETL-решению, но и провести череду согласований. Для этого потребовалось дождаться обновления архитектуры, получить различные доступы и создать новые ТУЗы. Кроме того, здесь мы тоже обеспечили обратную совместимость, чтобы ETL-решение забирало данные из MS SQL. Когда подготовили ETL к изменениям, то переключили коннекшен на PostgreSQL и стали забирать данные из нового места.
Что ещё? В монолите можно было получать любые данные одним запросом. После распила монолита данные приходится забирать из одного микросервиса, из второго, третьего… А потом их как-то объединять. Это не всегда просто. В такой ситуации может подойти решение наподобие Data Warehouse, которое могло бы агрегировать все данные. К такому решению мы в будущем придём.
Вжух — и у вас микросервисы (нет)

Какое-то время вы будете жить с гибридной архитектурой. У вас уже появятся микросервисы, выпиленные из монолита или созданные с нуля, но монолит вы ещё продолжите поддерживать параллельно. Таков путь. Что можно посоветовать напоследок тем, кому только предстоит переход на микросервисную архитектуру с монолита?
Определите цель перехода. С этого нужно начать и сразу понять, зачем конкретно вам стоит переходить на микросервисы. Потому что модно распиливать монолиты — недостаточно веская причина. А вот если вы правда столкнулись с проблемами масштабирования и отказоустойчивости, если вам нужен гибкий технологический стек или все эти проблемы назрели разом — тогда нет смысла держаться за монолит и стоит правда сменить его на микросервисную архитектуру.
Определите объём работ и готовьтесь к тому, что это будет долго. Переход на микросервисы может занимать месяцы, а в каких-то случаях — даже годы.
Определите этапы и контрольные точки перехода. Например, стартовый этап — монолит, потом распределённый на модули монолит, а затем это будут уже собственно микросервисы.
Поддерживайте работоспособность старых методов. Бывает, что микросервис по какой-то причине не завёлся — и вот тогда можно вернуться к работе этой функциональности в монолите.
Заранее переработайте код монолита, распилите его на различные модули, чтобы было легче переносить функциональность.
Обеспечьте мониторинг и логирование каждого микросервиса. Нужно будет следить за тем, что каждый микросервис работает и проблем не возникает. Если что-то не так, то, возможно, потребуется развернуть дополнительные экземпляры микросервисов для снижения нагрузки.
Ведите документацию. Каждый микросервис должен быть описан: как минимум его назначение, архитектурная схема, интеграции с другими микросервисами.
Вот, пожалуй, и всё, что мы с коллегами собирались рассказать. Надеемся, наши мысли и опыт помогут вам подготовиться к распилу монолита, заранее учесть возможные проблемы и успешно пережить переход на микросервисы. Если у вас ещё остались какие-то вопросы, пишите в комментариях. А если вы уже сами столкнулись с распилом монолита — тоже комментируйте, делитесь своими болями и найденными решениями.

