Общий подход разбиение монолита на компоненты

preview

Основной темой данного стрима будет "компонентно ориентированная декомпозиция". Как и любая декомпозиция - это лишь часть процесса построения архитектуры приложения, в задачи которой входит уменьшение зацепления компонентов между собой, в некоторых случаях для этого требуется изменение компонентной структуры приложения.

Монолиты часто представляют собой сильной зацепленные компонентно ориентированные приложения, поэтому сам по себе компонент не является признаком хорошей декомпозиции.Для уменьшения зацепление используется подход "извлечение компонент" и "ветвление (дублирование) компонент".

В первом случае мы постепенно уменьшаем зацепление путем точечного выделения компонента из всего приложения, во втором делается копирование всего монолита и удаляется лишнее.

Уход от монолитной архитектуры требует грамотной декомпозиции на компоненты, мы рассмотрим следующий алгоритм:

  • Оптимизация кодовой базы
  • Определение силы зацепления
  • Проведение архитектурных границ
  • Повышение уровня абстракции интерфейсов
  • Выделение "доменов"
  • Переход на модульную компонентную основу

Чем "архитектура" отличается от "декомпозиции"

Duration: 10

Часто на митингах звучит фраза "а теперь давайте обсудим архитектуру проекта" и далее начинается обсуждение того как нужно правильно разделить программу на классы и каким образом выстроить файловую структуру проекта. На самом деле речь идет не об архитектуре, а о декомпозиции, почему так? Давайте разбираться.

Определения:

Декомпозиция — операция мышления, состоящая в разделении целого на части. Также декомпозицией называется общий приём, применяемый при решении проблем, состоящий в разделении проблемы на множество частных проблем, а также задач, не превосходящих суммарно по сложности исходную проблему, с помощью объединения решений которых, можно сформировать решение исходной проблемы в целом. (ист. википедия)

Архитектура программного обеспечения — совокупность важнейших решений об организации программной системы. Архитектура включает:

  • выбор структурных элементов и их интерфейсов, с помощью которых составлена система, а также их поведения в рамках сотрудничества структурных элементов;
  • соединение выбранных элементов структуры и поведения во всё более крупные системы;
  • архитектурный стиль, который направляет всю организацию — все элементы, их интерфейсы, их сотрудничество и их соединение. (ист. википедия)

На самом деле определение архитектуры приложения имеет много трактовок, но все так или иначе сходятся в том, что архитектура - это выделение главного и принятие решений о функционировании системы в целом. Процесс выработки архитектурного решения, как правило, состоит из следующих шагов: - формулирование требований (ограничения, желаемые свойства системы и т.д.) - анализ и выработка "правил" функционирования программной системы (общие архитектурные решения, которые помогают принимать решение при написании кода или построении архитектурных представлений) - подготовка архитектурных представлений (как правило графическая декомпозиция системы на графические представления, разделенные на уровни абстракции) - анализ конечных свойств системы (проверка на соответствие требования и ограничениям).

Таким образом "декомпозиция" действительно используется как составной компонент построения архитектуры программной системы, но само понятие архитектуры гораздо шире, чем само понятие декомпозиции. Так же важно понимать, что архитектурная декомпозиция строится как правило на базе функциональной декомпозиции, а не модульной или классовой.

Таким образом, если речь идет о разделении программы на классы (не о выделении интерфейсов, не о распределении обязанностей, не о выработке требований и т.д.), а только о "географическом" размещении файлов и вынесении кода в эти файлы, то это с большим натягом можно отнести к процессу построения архитектуры приложения, но правильнее всего называть "модульная декомпозиция" или "классовая декомпозиция". Архитектуре же строится по принципу "от общего к частному" и начинается со сбора и формирования требований.

Оптимизация кодовой базы

Duration: 7

При работе с готовым монолитным приложением в первую очередь нужно понять размер технического долга (рассмотрим на отдельном стриме) и качество построенной архитектуры. Самое простое, что можно сделать для оценки качества архитектуры - построить "температурную карту приложения".

Температурная карта - это схематическое отображение компонентов приложения в виде прямоугольник окрашенных в градацию цвета от зеленого до красного. При этом:

  • размер прямоугольника определяется количеством строк в компоненте;
  • температура (цвет) определяется по метрикам оценки "Модифицируемости компонента":
    • цикломатической сложность;
    • глубина наследования;
    • количество условных выражений;

Температура выставляется взвешенно относительно медианного значения по проекту.

Задача: визуализировать приложение с позиции качеств кода, выделить наиболее и наименее перегруженные места. Понять насколько сбалансирован код, в контексте модифицируемости (см. стрим "Основные характеристики качества")

Желательно выводить оценку компонент на основе средств статического анализа и определить оценку "температуры" как фитнес функцию. Тогда при перестроении приложения новая карта будет обновляться автоматически.

Решение: рефакторинг кода с целью нормализации "температурной карты"

Определение силы зацепления

Duration: 7

После оптимизации кодовой базы у нас получается монолитная система с более удачным уровнем грануляции компонент, сбалансированных по сложности и размеру. Но остается примерной такой же уровень зацепления, который и был. Поэтому следующим этапом выполняется шаг по определение и уменьшению силы зацепления.

Задача: выявить компоненты с наибольшим уровнем зацепления. В стриме по Основным характеристикам качества мы говорили что оптимальный уровень зацепления - 2-3 компонента, все что более 7 - уже плохо.

Решение: оптимизировать зацепление. Наиболее эффективные способы управлять зацеплением: путем уменьшения силы связи ("композиция", "использование", "знание"); разнесения обязанностей выделение переиспользуемого кода использовать принципы DI (Dependency Injection)

Проведение архитектурных границ

Duration: 7

После того, как выполнены предыдущие два шага, у нас получается хорошо спроектированное монолитное приложение, которое не имеет явных проблем с основными характеристиками качества. На следующем этапе необходимо провести четкие архитектурные границы (частично они уже есть, но теперь их нужно провести явно).

Задача: провести архитектурные границы, изолирующие функциональность приложения.

Решение: объединить компоненты в модули и сформировать четкие интерфейсы взаимодействия; оформить графическое представление с учетом архитектурных границы (например, используя С4); * определить пространство имен (может проходить по границе модуля).

Повышение уровня абстракции интерфейсов

Duration: 7

Обычно в монолитных приложениях используется прямое обращение ко всем сущностями приложения (например, прямое обращение к методам класса). Исключением, как правило, является обращение к СУБД, которе делается либо через драйвер, либо дополнительно используется прослойка ORM.

Задача: выявить интерфейсы на уровне модулей и поднять их уровень абстракции.

Решение: повышение уровня абстракции интерфейса, как правило, достигается за счет перехода от интерактивных механизмов взаимодействия к реактивным. Наиболее популярен механизм "Pub/Sub", где есть источник, приемник и медиатор (шина). Для использование интерфейса унифицируется формат передачи данных и способы сериализации данных (бинарные или текстовые).

Выделение "доменов"

Duration: 7

Следующим шагом модули формируются в доменную стркутру - где каждый домент это по сути "микромонолит", т.е. либо сервис, либо микросервис. Выполняющий свою собственную бизнес-логику (не путать с DDD).

Задача: подготовить компоненты к раздельной дистрибуции на серверные мощности

Решение: гранулировать домены по границам сервиса; создать собственные источники данных в рамках каждого домена; реализовать ESB использование шаблона "Database per Service"

Переход на модульную компонентную основу

Duration: 7

Финальным шагом выполняется проектирование разделенного монолита под масштабирование нагрузки.

Задача: выполнить подготовку инфраструктуры для развертывания модульного приложения

Решение: использование шаблонов для организации транзакционной логики в распределенных системах (например, SAGA); использование механизмов балансировки и маршрутизации сообщений; * использование реактивных принципов построения приложения