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