От монолита к микросервисам. Монолитная модель данных. Распознать и обезвредить
Привет! Меня зовут Светлана Уварова, я архитектор информационных систем.
Микросервисная архитектура не гарантирует модульность, если в системе остаются монолитные данные. В этой статье разберемся, как модель данных может незаметно превратить микросервисы в монолит — и как этого избежать на этапе проектирования.
Ранее я уже писала, что архитектору важно понимать, какие данные будут использоваться в системе, как и кем они будут настраиваться, откуда данные собираются и куда передаются.
А если вам уже приходилось переводить монолит в микросервисы — или вы только планируете это сделать — вы могли и можете столкнуться с монолитной моделью данных (далее — ММД).
Неважно, идет ли речь о миграции данных или о ручном заполнении оператором: если вовремя не выявить ММД, в итоге получится новый монолит — только на микросервисном коде. Он будет иметь все характерные для него свойства, в том числе:
зависимость в выборе способа реализации одной функции от другой;
увеличенный срок поставки функционала из-за необходимости дополнительных согласований изменений;
рост объемов регрессионного тестирования и настройки в некоторых случаях.
И это совсем не те свойства, которые хотелось бы получить от микросервисной архитектуры.
Признак 1: Одна сущность в нескольких контекстах
Как правило, монолитная модель данных передается по наследству из монолитной системы, кажется, что те же самые процессы можно и нужно реализовывать на уже проверенной модели данных. В монолите реализуется несколько контекстов — основных функций, и зачастую на общей модели данных. Если переиспользовать эту модель, то в микросервисной системе появляются общие сущности, а функциональный контекст «разъезжается» по разным микросервисам.
Поясню на примере сервисного каталога, о котором я писала в своей статье «Как нефункциональные требования влияют на архитектуру». Все примеры ниже условные, но максимально приближенные к реальным ситуациям.
Условный пример — монолитная информационная система, в которой реализованы следующие контексты:
Контекст «Управление продуктом»;
Контекст «Управление сервисом»;
Контекст «Управление ресурсом»;
2. В этом примере в монолите используется общая сущность «Услуга»
Если переиспользовать общую сущность в микросервисной архитектуре, любые ее изменения и методы управления придется согласовывать с другими пользователями этой сущности — с разработчиками других микросервисов. Это, в свою очередь, приведет к задержкам поставок, а иногда — к невозможности реализации изменений. В результате проявятся монолитные свойства: зависимость между командами и увеличенный срок вывода нового функционала.
Признак 2: Типизация сущности для разных алгоритмов обработки
Бывают кейсы, когда без типизации сущности не обойтись — например, если такая информация необходима для отчётности. Но если типизация вводится лишь для того, чтобы по-разному обрабатывать разные экземпляры сущностей, это может быть признаком: пора либо выделить новый контекст, либо создать новую сущность.
Приведу пример неудачно типизированных сущностей. «Услуга» может относиться к разным типам — «скидка» или «блокировка». И скидку, и блокировку обычно клиент либо подключает, либо отключает. Т.к. сущность одна, действие с ней одинаковое — подключение (отключение), то логично реализовать единый интерфейс для управления экземпляром «Добавление услуги» и «Удаление услуги». В результате код, обрабатывающий такую сущность, начинает ветвиться: для скидки используется один алгоритм — добавление услуги и начисление скидки, для блокировки другой — добавление услуги и блокировка на оборудовании. Постепенно программный код, обрабатывающий сущность «услуга», обрастает разными работающими ветками. А при изменении только одной ветки тестировать на регресс придется все. Следовательно, стоимость и сроки регресса растут, а это опять совсем не то свойство системы, которое мы ожидали от микросервисов.
Выход из этой ситуации один: внимательно смотрите, для чего вы вводите типизацию. Если она нужна только для разной обработки экземпляров сущностей — лучше подумать о развитии модели данных и выделении новых сущностей, каждая из которых будет иметь собственную, независимую логику обработки.
Признак 3: Непонятное поведение и смысл экземпляров сущностей
Встречаются такие конструкции в моделях данных, когда очень сложно понять, что же означает конкретный экземпляр сущности, и как он будет себя вести. Причина может быть в том, что одна и та же сущность используется в реализации нескольких контекстов (см. п1). И определение контекста реализуется через справочники, маппинги, настройки.
В этом примере показаны разные по смыслу и поведению экземпляры сущностей, описываемые одинаковой структурой:
Код услуги | Название | Дополнительное пояснение |
001 | Ежемесячная плата | Это агрегат, помогающий вычислить и получить оплату |
002 | Скидка 2% на SMS | Это размер скидки и алгоритм, снижающий ежемесячную плату |
003 | Пакет голоса 200 минут в месяц | Это объемы и команда на оборудование оператора–подключить эти минуты |
Достаточно поменять названия и пояснения, и станет совершенно непонятно, как поведет себя конкретный экземпляр. Обращаю внимание, что эти текстовые атрибуты никак не гарантируют конкретные свойства экземпляров.
Конечно, здесь можно типизировать данные — тогда смотрим пункт 2. Возможен, например, такой вариант: вводится справочник с маппингом кода услуги на алгоритмы ее обработки. Казалось бы, отличная идея. Но представьте, что система развивается, наполняется данными, число услуг переваливает за тысячу. Теперь нужно не только обеспечивать работу алгоритмов, но и следить за тем, чтобы они не мешали друг другу. В такой ситуации рано или поздно возникнет комбинация услуг, которую не удалось протестировать — из-за огромного количества возможных сочетаний. И эта комбинация может повести себя совершенно непредсказуемо.
Затраты на настройку и поддержку такого решения могут стать очень большими. А чтобы понять, как в итоге поведет себя экземпляр или комбинация, может потребоваться даже заглянуть в код.
Из-за высоких затрат на настройку сроки вывода функционала увеличиваются. А иногда объем настроек и вовсе не оправдывает ожидаемых выгод от нового функционала. И самое интересное — можно реализовать систему, которая в итоге поведет себя неожиданно. Получается парадокс: увеличение срока вывода в продуктив — это как раз то, чего хотелось избежать на этапе проектирования.
Избежать подобной ситуации можно, если заранее выделить отдельные сущности: для скидок, для цен, для команд на подключение объемов на оборудование. А в нашем примере — дополнительно и для общих параметров.
Например, сущность «цена» и ее параметры: период и сумма. Сущность «скидка» и ее параметры: период, размер, суммируется или нет с другими скидками, абсолютная она или в процентах.
Что делать, чтобы микросервисная система не обрела свойства монолита из-за модели данных?
Если вы не хотите столкнуться с типичными свойствами монолита, такими как:
высокая связность компонентов,
длительные сроки реализации и вывода в продуктив,
высокая стоимость регресса и трудоёмкая настройка,
— не переносите модель данных из монолита в микросервисную архитектуру.
Вместо этого разработайте новую модель, адаптированную под микросервисы, и заранее продумайте алгоритмы миграции данных.
Вот несколько правил, которые могут существенно сократить шансы использования ММД в вашей системе.
Избегайте типизации данных в одной сущности для определения алгоритмов обработки экземпляра;
Не используйте общие сущности в разных микросервисах, проектируйте сущность так, чтобы она максимально отражала свою суть и поведение, особенно важны точные и понятные названия;
Общайтесь с командой. Рассказывайте о правилах заполнения экземпляров сущностей, делитесь информацией с архитекторами, разработчиками, тестировщиками — и, что особенно важно, с операторами, которые будут вводить данные в систему;
Уделите время для написания хорошей документации и кратких понятных инструкций по наполнению и миграции данных с картинками. Документация иногда может быть полезнее тысячи слов ☺, ведь многие айтишники — интроверты, которые любят почитать.
Удачи на пути построения микросервисной архитектуры!