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