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

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

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

Естественный вопрос, который возникает — как разделить набор функций системы на обособленные куски, пригодные для вынесения в подсистемы и до какого размера следует производить дробление?

Вопрос важный, потому что если на него ответить неправильно в начале декомпозиции, то вместо решения проблемы, можно прийти к ее усложнению. И тогда монолит, превратившийся в неконтролируемый “большой ком грязи” (BBOM — big ball of mud) может просто превратится в “распределенный ком грязи”.

Встречаются разные мнения по поводу определения размера:

  • размер микросервиса определяется размером команды, его реализующим;
  • микросервис должен выполнять одну функцию (do one thing, single responsibility);
  • размер микросервиса должен позволять легко его заменить в случае необходимости;
  • микросервис должен представлять минимальный жизнеспособный продукт (minimum viable product);
  • микросервис должен “легко укладываться в голову”.

 

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

В то же время, существует достаточно формальный способ подойти к определению размера микросервиса. В 2004 году, Эриком Эвансом была написана книга “Предметно-ориентированное проектирование”, в которой автор сделал подборку хорошо зарекомендовавших себя подходов к структуризации сложных систем.

В книге, в частности, популяризируется паттерн “Агрегат”, который предлагается в качестве способа обеспечить соблюдение инвариантов (или бизнес-правил), относящихся к тесно связанным группам объектов. В этом качестве он успешно и применялся в архитектуре ПО, в составе “обычных” — т.е. монолитных систем. Пока не появились микросервисы. И тогда оказалось, что предложенные методы декомпозиции можно успешно применять в рамках нового подхода.

Для примера, рассмотрим модель заказа товара. В объектной декомпозиции такой системы у нас могут появиться следующие классы:

  • Покупатель (Customer)
  • Товар (Product)
  • Заказ (Order)
  • Позиция заказа (LineItem)
  • Платеж (Payment)
  • Адрес (Address)
  • Накладная (Invoice)

 

Если мы хотим реализовать данную систему на основе микросервисов, то на основании вышеприведенных подходов, не очень понятно, какие микросервисы должны быть выделены. Какую одну функцию они должны выполнять? Что является минимальным жизнеспособным продуктом?

Теперь давайте попробуем выделить агрегаты. В этом нам помогут следующие положения:

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

Исходя из этого, у нас может получиться следующая схема агрегатов:

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

Что нам дает этот паттерн в применении к микросервисной архитектуре? Можно заметить следующее:

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

Такой подход хорошо зарекомендовал себя на практике. При этом хотелось бы обратить внимание, что в зависимости от бизнес-требований может оказаться, что практичнее его размер увеличить — объединяя два и более агрегата, если это обусловлено реальными (именно реальными) требованиями обеспечения атомарной целостности данных в пределах одной операции. Т.е. агрегат является нижним пределом размера микросервиса, меньше которого дробить уже становится вредно. Верхним пределом, до которого мы можем теоретически довести размер микросервиса, не возвращаясь снова к монолиту, будет являться “ограниченный контекст” — еще одно понятие из “Предметно-ориентированного проектирования”.

В большинстве же случаев, для поддержания целостности при операциях, затрагивающих несколько агрегатов (микросервисов) вполне оправдано применение событийной консистентности (eventual consistency). Это интересная тема, которая заслуживает отдельного рассмотрения.

  • Sharing

    Facebooktwittergoogle_plusredditpinterestlinkedinmailby feather