Как еще определять границы микросервисов
На нижеследующие мысли меня навела статья с говорящим названием "Microservices antipatterns and pitfalls", которую написал Mark Richards. Особенно интересными мне показались дополнительные способы проверки границ микросервисов. Если вы собираетесь переходить или уже перешли на микросервисную архитектуру, то вам должно понравиться.
Объем функциональности сервиса
В чём заключается функциональность сервиса? Письменный или устный ответ на этот вопрос — отличный способ понять, перегружен ли сервис. Чем чаще в описании встречаются соединительные союзы "и", "а также", тем выше вероятность, что сервис имеет слишком много обязанностей.
Этот способ оценки мне напоминает школьные задачи по логике, которые формулируются в стиле "найди лишний элемент в последовательности". И, действительно, это очень хорошее и эффективное упражнение, которым я часто пользуюсь. Выписываешь операции какого-либо сервиса и смотришь, что выбивается из общего ряда.
При этом важно принимать во внимание то, насколько прочны логические связи между перечисленными функциями сервиса, насколько они логически согласованы и сочетаются друг с другом. Если изъянов нет, то процесс декомпозиции выполнен успешно и нет причин для беспокойства. (По этой теме рекомендую обратить внимание на "Принцип каскадного снижения связанности", сформулированный Русланом Сафиным.)
Без преувеличения, это достаточно мощный аналитический приём, который следует использовать крайне хладнокровно, принимая во внимание и другие факторы. Пожалуй, более разумно начинать с грубой декомпозиции и крупных сервисов, а к деталям переходить позже, по мере улучшения понимания предметной области и знаний о работе системы.
Необходимость распределенных транзакций
Все ли операции сервиса умещаются в единую ACID-транзакцию? Если нет, то это повод задуматься, ведь иначе придётся самостоятельно обеспечивать согласованность и изоляцию данных, а это серьёзно усложняет решение. Распределённые транзакции, workflow-оркестраторы, саги, inbox/outbox-шаблоны, eventual consistency, идемпотентность и т.п. Действительно ли получаемые выгоды стоят того?
Или стоит более пристально взглянуть на существующие потоки данных и выявить изъяны? Возможно, те данные, которые находятся в стороннем сервисе, должны находиться в нашем. Возможно, стороннему сервису достаточно иметь реплику какой-то части данных нашего сервиса. Конечно, тут могут быть разные варианты, но это отличный способ посмотреть на существующее решение под другим углом.
Тут стоит внести важное дополнение. Часто наличие ACID является сдерживающим фактором декомпозиции сервиса, даже в те моменты, когда она действительно нужна. В итоге получаются распределённые монолиты, которые лишаются преимуществ и ACID-транзакций, и микросервисов.
Степень общительности сервисов
Насколько много внешних сервисов задействованы при выполнении операций целевого? Это в какой-то степени допустимо для API-шлюзов и workflow-оркестраторов, но в иных случаях это "черная метка". Каждое обращение к внешнему сервису снижает пропускную способность и надёжность.
Наличие подобной ситуации — признак неправильной декомпозиции и/или чрезмерной детализации. Возможные и не исключающие друг друга варианты решения — укрупнение сервисов путём их объединения и пересмотр их границ (bounded context). Улучшить, но не исправить ситуацию, может замена синхронного взаимодействия на асинхронное.
Соответствие целям бизнеса
Для чего делается сервис? Какую проблему бизнеса он решает? Ответы на эти вопросы могут существенным образом повлиять на итоговое решение. Например, если в основе требований бизнеса — уменьшение TTM (Time-To-Market), то будет стремление к множеству мелких сервисов; если повышение надёжности, то будет стремление к укрупнению сервисов.
Марк Ричардс рекомендует перед началом каждого обсуждения выписывать основные цели бизнеса, чтобы они всегда находились в поле зрения. Таким образом, любая идея, предложение или инструмент всегда будут проходить верификацию на соответствие целям.
В развернувшейся дискуссии по поводу этого пункта было правильно подмечено, что тема микросервисов не связана напрямую с темой надёжности. Действительно, эти понятия лежат в разных плоскостях. Также справедливо указано, что "микросервисы нужны не только и не столько для сокращения TTM, а для оптимального масштабирования, где монолит очень плох, а микросервисы очень хороши". Однако, посыл Марка был немного в другом, чем может показаться на первый взгляд. Например, транзакции "спасают" монолиты от многих неудобств, которые есть в распределенных системах. Жирненький микросервис (позвольте его называть макросервисом) до определённых пор может быть разумным компромиссом. Именно поэтому в погоне за "надёжностью" (за счёт транзакций), скорей всего, получится макросервис. Главное потом вовремя ретироваться, когда появится шанс. :)
А что для вас является маркерами, когда дело доходит до декомпозиции сервисов?
P.s. Посмею напомнить, что у меня есть Telegram-канал "Архитектоника в ИТ", где я публикую материал на похожие темы примерно раз в неделю. Подписчики меня мотивируют, но ещё больше мотивируют живые дискуссии, ведь именно в них рождается истина. Поэтому подписывайтесь на канал или на мой блог здесь. Будем обмениваться опытом и мнениями. ;-)