Данным постом я начинаю цикл статей про моделирование микросервисов. В первой части мы поговорим про качественные характеристики границ микросервисов. В следующей статье мы обсудим предметно-ориентированный подход и методы выделения границ. В третьей, заключительной, мы рассмотрим альтернативы предметно-ориентированному подходу, а также обсудим смешивание разных моделей и исключений. Все статьи цикла призваны улучшить ваше представление об оптимальном разделении системы на модули в рамках микросервисной архитектуры.
Ключевой идеей микросервисов является возможность независимого изменения и развертывания отдельно взятой функциональности. Фактически, микросервисы - это еще одна форма модульной декомпозиции. Навыки правильного выделения границ модулей являются ключом к построению успешной микросервисной архитектуры. Выделяют три ключевые концепции, определяющие качество границ микросервисов.
Инкапсуляция
Инкапсуляция подразумевает сокрытие как можно большего количества деталей реализации внутри границ микросервиса. На внешних границах микросервиса допустимо размещать детали реализации, необходимые для связи с другими сервисами. Они должны являться внешним контрактом микросервиса.
Связи между модулями - это предположения, которые модули делают друг о друге (Дэвид Парнас)
Снижая количество предположений, которые один модуль (микросервис) делает относительно другого, мы напрямую влияем на связи между ними. Сохраняя число предположений наименьшим, легче гарантировать, что мы сможем изменить одну часть, не затрагивая другую. Этот подход усиливает основные преимущества микросервисов: ускорение разработки, прозрачность и гибкость.
Связность
Под связностью (cohesion) подразумевается сгруппированность функциональности таким образом, чтобы иметь возможность вносить изменения в как можно меньшем количестве мест. Мы оптимизируем микросервисную архитектуру с целью простоты преобразований бизнес-функциональности и идеальным сценарием является размещение одновременно изменяемых частей в одном модуле (микросервисе).
Код, в который вносят изменения как в единое целое, остается единым целым (Сэм Ньюмэн)
Если для решения очередной задачи бизнеса нам потребуется вносить изменения по всей системе, можно констатировать, что связность функциональности слабая.
Связанность
Связанность (coupling) представляет собой степень взаимодействия между модулями. Когда между модулями наблюдается слабая связанность, изменения в одном модуле не требуют изменений в другом. Слабо связанный сервис имеет необходимый минимум сведений о сервисах, с которыми ему приходится работать.
Структура стабильна, если связность сильная, а связанность слабая (Ларри Константин)
Если связанная функциональность распределена по всей системе, изменения в этой функциональности будут распространяться через границы модулей, что подразумевает более сильную связанность.
Связанность и связность тесно переплетены. Связность применима к отношениям между функциональными объектами внутри границ модуля. Связанность представляет отношения между объектами через границы модулей.
Определенный уровень связанности будет присутствовать в системе всегда. Следует следить за ростом связанности при развитии системы. И, по возможности, уменьшать ее. Для этой цели рассмотрим широко распространенные типы связанности, отранжированные от слабых и желательных до сильных и нежелательных.
Доменная связанность
Доменная (предметная) связанность описывает взаимодействие микросервисов, когда одному требуется функциональность, предоставляемая другим. Доменная форма связи считается наиболее слабой формой связи. Однако ситуация, когда микросервису необходимо взаимодействовать с большим количеством нижестоящих микросервисов, может свидетельствовать о чрезмерной централизованности логики в одном месте.
Временная связанность
В контексте распределенной системы временная связанность подразумевает зависимость во времени работы одного сервиса от другого. Например, один сервис для завершения своей операции должен дождаться выполнения операции другим сервисом. По мере роста количества микросервисов и усложнения синхронного взаимодействия между ними, будут нарастать трудности с масштабированием и надежностью системы. Один из способов снижения временной связанности - использовать форму асинхронной связи, такую как брокеры сообщений.
Сквозная связанность
Сквозная связанность подразумевает ситуацию, когда один микросервис отправляет данные второму микросервису исключительно для того, чтобы тот передал эти данные следующему. Это наиболее проблемная форма связанности. Она подразумевает, что сервис должен знать, что вызываемый им сервис вызывает еще один. А также то, что сервис должен иметь предположения о том, как работает удаленный от него микросервис. Основная проблема сквозной связанности заключается в том, что изменение в данных нижележащего сервиса может привести к более значительному изменению в вышестоящих сервисах.
Есть несколько способов борьбы со сквозной связанностью. Первый - рассмотреть возможность для вызывающего микросервиса обходить посредника и обращаться напрямую к нижележащему микросервису. Этот подход имеет побочный эффект в виде усложнения логики вызывающего сервиса. И он не всегда применим в случаях, когда промежуточный сервис также принимает участие в обработке до или после завершения работы нижележащего микросервиса. Второй способ заключается в сквозной передаче требуемых данных без чтения и модификации промежуточным сервисом. То есть, промежуточный сервис относится к этой части информации как к неструктурированной. И в случае изменения этих данных, вносить изменения в промежуточный сервис не потребуется.
Общая связанность
Общая связанность возникает в ситуации, когда два или более микросервиса используют общий набор данных. Наиболее распространенным примером такой связанности могут служить микросервисы, использующие одну и ту же базу данных. Также, такая форма связанности может проявляться при использовании общей файловой системы.
Основная проблема такой формы связанности заключается в том, что изменение в структуре данных может повлиять на несколько микросервисов одновременно. Наиболее разумным здесь будет решение о выделении работы с общими данными в отдельный микросервис. Сервисы, которым потребуется получить или изменить общие данные, будут обращаться к данному микросервису. Теперь он будет ответственным за управление состоянием данных. Для того, чтобы состояние объектов данных изменялось правильным образом, следует создавать конечные автоматы. Конечный автомат может использоваться для управления переходом некоторого объекта из одного состояния в другое, запрещая недопустимые переходы состояний.
Второй проблемой общей связанности является конкуренция за ресурсы. Множество микросервисов, использующих одну и ту же базу данных или файловую систему, могут перегружать общий ресурс, вызывая серьезные замедления в его работе, вплоть до отказа. Наличие общей связанности в системе свидетельствует о плохой связности в коде проекта. Об этом стоит задуматься.
Связанность по содержимому
Связанность по содержимому возникает, когда вышестоящий сервис проникает во внутренние компоненты нижестоящего сервиса и изменяет его состояние. Наиболее ярким примером служит ситуация, когда внешний сервис обращается напрямую к базе данных другого микросервиса и вносит в нее изменения. Различия между связанностью по содержимому и общей связанностью небольшие. В обоих случаях, два или более микросервиса выполняют чтение и запись одного и того же набора данных. При общей связанности используется общая внешняя зависимость. При связанности по содержимому, общий ресурс, как правило, представляет собой часть внутреннего представления одного из сервисов.
Когда один сервис разрешает внешней стороне прямой доступ к своей базе данных, она фактически становится частью внешнего контракта. Теряется способность определять, что относится к общим ресурсам, а что скрыто. Скрытие информации вышло из под контроля. Связности по содержимому следует избегать.
Заключение
В этой статье мы обсудили качественные характеристики границ микросервисов. Понимание этих аспектов позволит извлекать преимущества как из слабой связанности, так и из сильной связности.
Детальное понимание предметной области может стать жизненно важным инструментом, позволяющим выделять эти границы. О подходах и методах выделения границ микросервисов мы поговорим в следующей статье. А пока подписывайтесь на мой телеграм-канал, чтобы ничего не пропустить. И до скорых встреч!