Антипаттерн Entity Service. Иногда микросервисы хуже монолита

    Статья об одном неудачном решении, которое распространено при переходе на микросервисы. Несмотря на то, что Microsoft и другие компании в своих руководствах рассматривают возможность создавать Entity Services, есть все основания считать его антипаттерном. Далее мы поговорим о том, что такое Entity Service и какими свойствами он обладает для конечной системы в целом.

    Оригинал находится по ссылке

    Entity Service - что это такое? И как возникает идея о его создании?

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

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

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

    На рисунке регистрация заказа и планирование доставки остались внутри фасада, но они могут быть вынесены в отдельные сервисы - в рамках нашего примера это не самое главное.

    Такие сервисы называют одним термином - Entity ServiceЭто сервисы, которые содержат только CRUD операции для одной сущности и ничего более.

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

    Все это ведет к следующим недостаткам:

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

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

    • Сложность отладки. Если сравнивать с монолитом, то все стало хуже. Теперь код находится в разных процессах и нужны инструменты вроде Jaeger Jaeger: open source, end-to-end distributed tracing.

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

    Несмотря на все это в документации по микросервисам от Microsoft рассказывается как сделать такой сервис: Creating a simple data-driven CRUD microservice

    Как избавиться от Entity Service?

    Для рассмотрения решения по рефакторингу entity service удобно рассмотреть другой пример. Допустим у нас есть интернет ресурс в котором авторы могут публиковать свои статьи (вроде блога или Хабр). Есть следующие возможности:

    • автор может предложить к публикации статью;

    • сайт проверяет уникальность иллюстраций, не встречаются ли эти картинки где либо еще;

    • сайт проверяет уникальность текста, учитывает семантику текста. Если он написан синонимами, но о том же - отсеиваем;

    • модератор сайта может вносить правки в статью (изменять форматирование заглавной иллюстрации, править текст ссылки);

    • модератор публикует финальную версию;

    • посетители сайта могут видеть список публикаций по категориям.

    Реализация с Entity Service может быть такая:

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

    Есть как минимум два способа избавиться от entity service. В первом случае мы обращаем внимание на поведение, а не на сущности и данные. Таким образом, мы пытаемся представить сам бизнес-процесс, где каждый из шагов в нем - отдельный сервис:

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

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

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

    • Каждый сервис получается маленьким и не содержит много кода.

    • Структура сервисов приближена к реальному бизнес-процессу.

    • Не возникает проблем с конкурентным доступом к одним и тем же данным в ходе выполнения бизнес-процесса.

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

    • Проще реализовать согласованность в конечном счете (eventual consistency).

    • Проще производить горизонтальное масштабирование. Например, если один из микросервисов очень ресурсоемкий.

    Недостатки тоже есть:

    • Потенциально сетевой трафик больше.

    • Нужно больше места на диске.

    Если вы еще не используете такой подход - есть подсказка. Вместо существительных в названии сервисов должны быть глаголы. Тогда сервисы будут в большей степени ориентированы на поведение, нежели на данные.

    Еще один вариант, на который имеет смысл обратить внимание - это выделение микросервисов по поддоменам. Pattern: Decompose by subdomain По ссылке выше есть еще варианты разбиения на микросервисы, но именно два вышеупомянутых имеет смысл рассмотреть в первую очередь.

    Оба варианта отлично сочетаются - вы вполне можете их совместить. Главное избежать всех недостатков Entity Service.

    Доводилось ли вам сталкиваться с данным антипаттерном в своей практике? Как бы вы предложили рефакторить системы, где он уже есть? Задавайте вопросы, высказывайте свои мысли в комментариях. Интересно ваше мнение!

    Средняя зарплата в IT

    120 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 9 207 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 48

      0

      .Тут, всё же, антипаттерн не сам Entity Service, а его неуместное применение.

      В примере с магазином вообще очень плохо вытаскивать на пользовательский уровень почти прямую работу с базой.

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

        +2
        Тут, всё же, антипаттерн не сам Entity Service, а его неуместное применение.

        А есть пример его удачного применения?
        В примере с магазином вообще очень плохо вытаскивать на пользовательский уровень почти прямую работу с базой.

        В примере подразумевалось либо наличие сервиса-оркестратора (на беке), либо логика живет как отдельные сервисы.
        На самом деле подобрать примеры не так уж просто. В них есть недостатки, но цель была другая. Донести общую идею… И с этим примеры вроде как справляются. Опять же если приводить примеры из жизни, то нужно будет погружаться в предметную облать — ОФЗ, Фискальные регистраторы и пр. и т.п. Поэтому предпочел именно эти примеры…
          +2
          Честно говоря не понял почему это антипаттерн.

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


          По моему это проблема любых распределенных систем. Тут только контроль за интерфейсами и схемами данных поможет.

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


          Отказоустойчивость лучше чем у монолита. Так не для этого ли я выделял функционал в отдельный микросервис? В чем тут антипаттерн?

          Сложность отладки. Если сравнивать с монолитом, то все стало хуже. Теперь код находится в разных процессах и нужны инструменты вроде Jaeger Jaeger: open source, end-to-end distributed tracing.


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

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


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

          А если версионировать данные, то и код надо версионировать. Через десяток лет там будет десяток версий для работы с разными версиями документов. Проще сжечь потом все и начать с белого листа.
            0
            По моему это проблема любых распределенных систем. Тут только контроль за интерфейсами и схемами данных поможет.

            Вопрос в том насколько остро эта проблема стоит в какой-то конкретной системе, в конкретной реализации. В случае с Entity Service, который только CRUD для сущности, эта проблема стоит максимально остро… Потому что почти любой алгоритм с участием такой сущности, любая бизнес логика завязаны на единую точку отказа. И если вы ломаете эту точку отказа — рушится много чего. Все что на нее завязано.
            Отказоустойчивость лучше чем у монолита. Так не для этого ли я выделял функционал в отдельный микросервис? В чем тут антипаттерн?

            Лучше. Но во-первых можно еще заметно лучше. А во-вторых все это улучшение нивилируется производительностью.
            Но ведь Entity Service это только CRUD. Зачем может понадобиться отлаживать CRUD? На практике ведь как получается у вас тормозит выставление счета. Там логика. И что не работает? Берем dotTrace или другой профайлер, смотрим. А тут какой-то из 10 сервисов может тормозить… Какой конкретно? С какими параметрами?

            По версионированию могу добавить, что переодически можно делать снепшоты. Это как один из вариантов.
            На самом деле мне реализация микросервисов с помощью Entity Service напоминает вывернутый наружу монолит… именно по этому отношу его скорее к антипаттернам и стараюсь при проетировании избегать.
            0
            У меня трекер сломался, не показывал обновления.
            А есть пример его удачного применения?
            В моём опыте — все варианты были хороши :)
            Например, из недавнего: переписывал я ядро системы, которая была кривеньким монолитом, и где из любой щели лезли прямые запросы в базу. Получилось собрать вместе SOA + SOLID + AOP. На верхних слоях были только бизнес-сервисы, а на нижних в основном были именно Entity Services. И было это хорошо :)

            В примере подразумевалось либо наличие сервиса-оркестратора (на беке), либо логика живет как отдельные сервисы.

            Так в том то и дело, что примеры должны быть релевантны задаче.
            Я, вот, не могу вспомнить ни одного магазина, где прямо с фронта идут запросы к Entity… Всё работает через бизнесовые сервисы.
          +1
          Потенциально выше производительность, т.к. у каждого сервиса есть своя копия данных.

          И в один прекрасный день эти копии перестанут совпадать.
            0
            Одинаковой версии? Нет, версионирование позволяет вам при изменнии сгенерированть новую версию данных. Предыдущая версия остается неизменной… Но опять же нужно исходить из цели. Предположим вы имеете в виду, что опубликована новая версия статьи. Тогда мы отправляем в шину либо новую версию, либо только измения (если нас беспокоит перфоманс). Остальные сервисы могут как оставлять старые данные нетронутыми, так и удалять их за ненадобностью в зависимости от логики.
            Что касается схемы с хранением одной версии данных в одном месте. Тогда мы получаем botleneck. Тут и при изменении схемы могут поехать другие сервисы да и масштабировать сложнее — в целом все это в статье упомянул. Какой из этих вариантов выбрать решать, конечно, вам исходя из той системы, которую делаете. Но я бы все таки как минимум рассмотрел альтернативы.
              +2
              Я не про то. Вы двумя разными сервисами пишете что-то одинаковое в две разные бд.
              Потом оказывается что данные в этих бд разные. Во всем многообразии возможных несовпадений.

              Так всегда бывает. Скорее рано чем поздно.
                0
                Вопрос в том где вы прочертите грань. Ну вот предполжим вы отправляете декларацию, которую составили у себя на компьютере в налоговую. А что если та версия в налоговой не будет совпадать с той версией что у вас на компьютере? Вы ведь всегда у себя на компьютере ее поправить можете?
                Хранилище то у этих копий разное… Как быть? А не как это нормально. Если вы отправили документ. То вы его отправили именно таким. Именно такой версии. И если вы хотите его обновить — шлете новую версию. И в микросервисах так же. Нужно их просто воспринимать как самостоятельный продукт. И это вовсе не значит, что нельзя будет согласовать взаимодействие.
                Все те же налоговые прекрасно работают и принимают документы круглый год. ;)
                  +1
                  Да, отправили. Да там получили. Но потом внезапно окажется что они разные. Или где-то нет. Хотя никто ничего не менял.
                  У налоговой для исправления есть процес сверки.

                  У ваших сервисов будет тоже самое. Так всегда бывает. Если одни и те же данные писать из разных мест в разные БД они никогда не совпадают.

                  Нужно запускать какие-то процессы сверки всех БД и решать где данные более правильные. Это часто очень нетривиально. Даже просто сравнить две таблички в разных БД размером гигов по 100 каждая уже непросто. А уж решить какая из них более правильная это вообще не всегда без человека возможно.

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

                  Часто лучшее решение это не дублировать данные. Транзакции и недублирование эти проблемы решают на корню. Они вообще не могут появится.
                    0
                    От системы зависит. Вот у нас конкретный пример. Есть статья. Есть логика про проверки на плагиат, которая выдает числовой коэффициэнт от 0 до 1. Степень плагиата… Ну вот разошлись версии. И что? Запускать процесс сверки? Исправить причину расхождения и отправить еще раз…
                    В какой-то учетной системе. Где деньги хранятся — да. Согласен. Но ведь ситуация такова, что какой-то простецкий сайт с текстовками начинают проектировать с использованием сверхтяжелых инструментов на serializable уровне транзакций. А это не всегда оправдано… ))
                    В целом я с вами согласен, что такая проблема есть. Но думаю сильно от системы зависит на какие мы можем пойти исключения.
                      0
                      Простенький сайт делается монолитом. Все остальное оверинжинеринг. Если что.

                      Есть логика про проверки на плагиат, которая выдает числовой коэффициэнт от 0 до 1. Степень плагиата… Ну вот разошлись версии. И что? Запускать процесс сверки? Исправить причину расхождения и отправить еще раз…

                      Не версии. Данные разошлись. В одном месте 1, в соседнем 0. Хотя все остальное одинаково. Чтобы это найти нужна сверка. Иначе вы просто не узнаете что у вас такое в данных. Удалить все и прогнать заново вариант. Если исходные данные не утрачены. А они могут быть уже утрачены за ненадобностью.

                      Но думаю сильно от системы зависит на какие мы можем пойти исключения.

                      Любую систему лучше делать так чтобы данные не расходились. Или чтобы это расхождение своевременно обнаруживалось и правилось.

                      Для нерасхождения есть только один варинат одна БД и транзакции в ней. Все остальное гарантий не дает. Этот вариант по умолчанию основной.

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

            Но абстрактный интернет магазин сильно сложнее и функциональнее абстрактного блога.

            Имхо, было бы всё таки намного полезнее узнать, как же всё таки правильно применять этот паттерн в примере с интернет-магазином, а не в блоге.
              0
              Я думал об этом при написании статьи. Мне показалось, что в случае с интернет магазином получился бы очень сложный пример и пришлось бы придумывать бизнес-логику. Применение пайплайна там само по себе более спорное решение и было бы много вопросов к публикации.
              А у меня цель была — саму идею донести максимально сжато без лишних деталей.
              Но все таки я подумаю что можно с этим примером сделать. Скорее всего напишу отдельную статью еще одну… но пока думаю как лучше… Так что на связи! :)
                0
                Понял! В любом случае за статью спасибо!)
                  0

                  Жаль, что в плохом примере вы взяли сложный магазин. Он хорош именно для объяснения, когда Entity Service верно используется. Microsoft не просто так продвигают этот паттерн. Он часть стандартов некоторых отраслей. В том же ритейле он уместен. CRUD применим к данным, а не процессам. В магазине есть каталог — набор данных о продукте. Сущность Item не имеет бизнес логики. Вот тут Entity Service и является классическим. Если сильно упростить то взаимодействие сервисов будет похоже на:
                  image
                  Entity Service только Item. Catalogue,Price, Inventory и еще куча всего — это уже бизнес и там простой CRUD уже действительно антипаттерн.

                    0
                    В таком случае будет CRUD у Item или просто на Read в основной массе? Каково, на ваш взгляд, обоснование выделение отдельного микросервиса? Все таки это оверхэд как минимум по производительности…
                    Т.е. почему бы каталог и Item не обьединить в одни сервис? (на примере вашей картинки)?
                      0

                      Управление данными именно Item. Это не только общие данные для других сервисов и база CQRS, но и логический-физический Entity. Банка колы не имеет фиксированной цены закупки или продажи, не обязана продаваться во всех магазинах, поставляется разными компаниями в разной таре (ящик, контейнер и тд), не имеет кода продажи (EAN). Но при этом имеет имя, код производителя (SKU), вес и тд. У веса нет бизнес процесса. У продажи может быть проверка весом на кассе самообслуживания. У банки нет условий продажи. В каталоге же уже будет цена и тип тары (продавать по единицам или пачками по 6, допустим). Так что без бизнеса — CRUD. Anemic model. При этом для консистентности данных нельзя создать банку сразу в каталоге, а потом в инветарь и тд. Каталог ожидает наличие Item.

                        0
                        Ну ок… вот видите как нам пришлось погрузиться в конкретные бизнес процессы. И вопросов на самом деле только больше стало. Задам все таки последнию порцию вопросов. Исходя из того что и как я понял.
                        Почему бы вам не обьеденить Catalog+Item+Inventory в один сервис. Предоставив наружу все то же самое что они сейчас предоставляют и съэкономив при этом на их текущем взаимодействии. Ведь там наверняка и так транзакционность обеспечивается? Сейчас у вас если упадет Item, то Inventory и Catalog так и так лежать тоже будут, т.к. на него завязаны.
                          0

                          Catalog, Item, Inventory — абсолютно разные вещи с точки зрения бизнеса. Это уже монолит. Классический и тяжелый.
                          Транзакционность есть, но не между сервисами. Я видел несколько реализаций подобных систем. Некоторые копировали данные себе, некоторые держали кэш, в одной была обратная связь — Item выдавал обновления всем подписчикам.
                          То есть можно продать, а инвентарь потом обновиться как оживет. В продаже тоже не прямая онлайн связь с Item, так как в ходе транзакции вещи не должны исчезать или меняться.
                          Самое главное — любая попытка привести пример к теоретическим выкладкам, заканчивает сферическим вакуумом. В реальности в сложной системе есть места разным паттернам. Они поэтому и возникают. Никто не форсит их из академического интереса. Сайт-магазин — это ещё очень лёгкий сценарий. В традиционном ритейле вообще куча домейнов и разных типов связей.

                            0
                            Здесь бы, конечно, поразбираться с конкретной системой. Возможно мне удалось бы переубедить вас что обьеденив 3 сущности мы не получим монолит. А быть может вы раскрыли бы все детали, которые просто невозможно раскрыть в рамках одного маленького сообщения и нашлось бы совсем иное применение.
                            Исходя из моей практике — не вижу места для Entity Service в микросервисной архитектуре. Всегда находится решение лучше… Если все обстоятельно взвесить.
                    0
                    У фотографов есть стандартная картинка (девушка в шляпе), на которой показывают/рассказывают про настройки/фильтры/преобразования изображений.
                    В ИТ аналог этой шляпы наверно все-таки магазин в простейшем варианте — все мы ходим в магазины и покупаем товары. И логика: товар-цена-чек-отгрузка понятны всем покупателям. Оставляя за скобками накладные, закупку, каталоги, мерчендрайзинг прости-хосподи! и прочий завитринный стафф.
                    Часто в книгах/статьях авторы используют домен блоггинга (статья, публикация, модерация), наверное потому что авторам это ближе. Но не факт что ближе читателям. Пишут-публикуют на порядки меньше тех, кто ходит в магазины.
                  +4
                  Как бы вы предложили рефакторить системы, где он уже есть? Задавайте вопросы, высказывайте свои мысли в комментариях. Интересно ваше мнение!

                  я часто сталкиваюсь с ситуациями, когда монолиты считают злом и изначально разрабатывают в микросервисах и кладут много ресурсов на это.


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


                  Мне кажется идеальная система развивается так:


                  1. делаем монолит (быстро, MVP)
                  2. добираем функционала на монолите
                  3. с ростом пользователей выявляем проблемы масштабируемости. решаем их. Например вынося какие-то функции в самостоятельные сервисы: например авторизацию налево, геофункции направо итп

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


                  а у различных микросервисов получается единая кодовая база. Что кто-то может считать минусом — является плюсом в скорости развития

                    0
                    Согласен! Не нужно везде делать микросервисы. Мы уже выше в комментариях обсуждали — примеры в статье больше для наглядности и простоты. Вовсе не значит, что реальную систему стоит делать именно так…
                    Сделать сначала монолит, а потом распилить на микросервисы? Я так делал и на самом деле за этим стоит непроработанность требований. Т.е. по факту вы не очень знаете как будет развиваться продукт — поэтому. Накладные расходы на N сервисов при шаблонизации хоть и выше, но не намного.
                      +1

                      непроработанность требований

                      а кто-то вообще сталкивался с проработанностью требований при запуске новых бизнесов (стартапов)?

                        0
                        Я сталкивался на самом деле на практике, такое случается. Хоть и очень редко…
                    +1
                    Entity Service — вполне ожидаемо плохое решение.
                    Потому как если посмотреть, как получаются эти самые сущности (entity) при декомпозиции предметной области (например методом ER-диаграмм/IDEF1), мы увидим, что изначально их там нет: они берутся из моделей данных для различных компонентов системы путем помещения их в общую БД и нормализации структуры БД. То есть, модель данных любого компонента системы структуре хранения данных почти никогда не соответствует: она почти всегда включает себя несколько сущностей. А структура хранимых данных не отрывается от моделей данных компонентов исключительно за счет того, что все эти сущности одновременно доступны в одной БД. Именно БД является способом сохранения необходимого соответствия структуры храненимых и обрабатываемых данных после декомпозиции.
                    Ну, а выделяя каждую сущность в отдельный сервис со своей БД, мы это соответствие разрушаем: структура хранения данных перестает соответствовать структуре обрабатываемых данных. Потому-то предложенные альтернативы оказываются лучше — ибо они соответсвие моделей обрабатываемых данных структуре их хранения не нарушают.
                      0

                      мы пробовали не применять подобный подход, но логгировать полностью данные сущностей: что с ними происходит.


                      то есть SQL-запрос изменяет в БД сущность и при этом в лог попадает изменённая сущность.


                      по данным — аналог того что предлагается.


                      так вот. сервис исполняет ~100 тыс заказов в сутки (чуть больше 1 в секунду)
                      ну и в постгре за неделю разгоняется где-то 20 гигабайт таблица.


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


                      При этом, таблицы надо сразу проектировать либо как большие, либо партицированные

                        0
                        Ну естественно нужно применять инструменты уместно и сразу оченивать обьемы. Вот заказы — 1 в секунду. А наверняка есть еще и товары, настройки, пользователи они меняются раз в день. Может даже реже… Почему бы не сделать ядро системы в одном стиле, остальную часть вынести и т.п. Ну во всяком случае рассмотреть такую возможность.
                        Потом версионирование ведь тоже по разному можно реализовать. Вот вы упомянули Постргрес. А вот чем Постгрес отличается от SQL Server в плане реализации транзакционности. Транзакции одинаково хорошо и тот и другой реализует. Только у Постгреса происходит работа с разными версиями (внутри движка) а у SQL Server блокировки. И при этом Потгрес не испытывает проблем, есть вакуум и т.п. Паттерн вроде бы один, а реализация может различаться. :)
                        0

                        Прошу прощения, но сама статья это один большой "антипаттерн". В статье я вижу два подхода, которые вообще не стоит использовать — разбиение по пользовательским данным и функциональности.


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


                        • А здесь мы зафигарим вот такой паттерн!
                        • А почему?
                        • Я его хорошо знаю.

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


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

                          0
                          Конечно, слепо применять паттерны не нужно. Всегда нужно анализировать каждый случай отдельно, учитывать специфику системы. С другой стороны я не вижу веских доводов использовать в своих системах Entity Service на систематической основе и везде — это ведет к проблемам, что здесь и описаны…
                          0
                          При реализации нового функционала вы можете сломать уже существующий
                          Это верно для любой абсолютно ситуации.
                          Допустим, вы меняете метод обновления заказа и удаляете оттуда один из параметров (либо добавляете)
                          Конкретно это — смена публичного контракта сервиса. Такое, будучи неаккуратно сделанным, приводит к проблемам в независимости от того Entity service это или нет.
                          Отказоустойчивость не очень высокая. Если падает все тот же сервис заказов — не работают все сценарии, в которых он участвует.
                          Тоже не совсем удачный пример. Здесь проблема не в том, что это entity service, а в том, что это явный боттлнек в одном инстансе без балансировщика нагрузки и прочего. А если вы хотели донести мысль что «любой entity service с практически 100% вероятностью = боттлнек --> требует доп. телодвижений для обеспечения отказоустойчивости», то это другая мысль и требует отдельного доказательства.
                          Сложность отладки. Если сравнивать с монолитом, то все стало хуже. Теперь код находится в разных процессах и нужны инструменты вроде Jaeger
                          Это верно вообще в целом если сравнивать монолит и микросервисы. В чем тут отличие конкретно entity service?

                          В общем я не понял ваши примеры по недостаткам. Будет здорово, если вы их как-то раскроете или перепишете.

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

                          В примере, где мы якобы избавились от entity service, последний в цепочке сервис каталога статей по тому, как он изображен, очень смахивает на чистый круд на базе, а значит он все еще entity service. Все еще плохо, или я не понял схему?

                          В общем и целом, я вынес, что проблема есть, а вот детали после прочтения не представляются в ясном виде. Надеюсь, что вы каким-то образом сделаете это яснее, т.к. тема явно важная. Так или иначе, спасибо.
                            0
                            Конкретно это — смена публичного контракта сервиса. Такое, будучи неаккуратно сделанным, приводит к проблемам в независимости от того Entity service это или нет.

                            Это так. Но! у Entitty Service большое, если не сказать огромное число связей. Вот в чем разница. А это значит, если вы меняете в нем контракт, то переписываете большое число фрагментов с бизнес-логикой.
                            Тоже не совсем удачный пример. Здесь проблема не в том, что это entity service, а в том, что это явный боттлнек в одном инстансе без балансировщика нагрузки и прочего. А если вы хотели донести мысль что «любой entity service с практически 100% вероятностью = боттлнек --> требует доп. телодвижений для обеспечения отказоустойчивости», то это другая мысль и требует отдельного доказательства.

                            Проблемы с отказоустойчивостью не всегда решаются только лишь наращиванием ресурсов. У вас может быть баг в коде (в каком-то глубоком ифчеке, который не дотестили) до тех пор пока вы не заметили на проде и пока не откатились — у вас страдает N сервисов. А как мы видели из примера особенность Entity Service такова, что он связан почти со всеми. Т.е. страдают все.
                            И даже в случае с производительностью. Но вот да — у вас проблема, да вы ошиблись с балансировкой и масштабированием и да у вас страдает от этого ПРОД. Одно дело если это какой-то изолированный функционал типа регистрации черновика публикации. А другое дело если это и регистрация черновика публикации. И проверка на плагиат публикации и проверка уникальности иллюстраций и даже сервис модерирования. Т.е. разница в масштабах катастрофы разительная просто…
                            Это верно вообще в целом если сравнивать монолит и микросервисы. В чем тут отличие конкретно entity service?

                            С Entity Service мы получаем много, много разных мелких сервисов и какой-то бизнес-сценарий, законченный логический алгоритм начинает ходить по этим сервисам.
                            Давайте возьмем наш простейший пример с публикациями. Вот если в результируем наборе сервисе мы сделали так: к нам на вход прилетает публикация, мы ее проверили и выстрельнули публикацию + коэф. похожести текста на другие источники в виде числа. Все происходит внутри сервиса. Верно?
                            А если бы у нас был Entity Service. Мы в сервисе проверки публикации на плагиат получили ИД публикации, слазили в сервис публикаций — вытащили оттуда публикацию, проверили ее, получили коэф. Далее дернули сервис публикации и обновлии поле с этим коэф. Т.е. у нас появились http вызовы в логике проверки. А здесь простейший пример и их всего два. В случае с более реальными примерами будет очень много вызовов и отлаживать такое неудобно… Представьте на секунду что у вас иногда тормозит обновление публикации (при определенных параметрах) у вас 20 таких сервисов. А жалоба к вам поступила вида «Что-то проверка на плагиат медленно работает»

                            Чтож, в целом вам спасибо за замечания… Подумаем, посмотрим что можно еще дописать. Дополнить :)
                              0
                              у Entitty Service большое, если не сказать огромное число связей

                              Наверное вы подразумевали что-то наподобие «у Entity Service очень часто получается неоправданно большое число связей, чего можно избежать»?
                              У вас может быть баг в коде (в каком-то глубоком ифчеке, который не дотестили) до тех пор пока вы не заметили на проде и пока не откатились — у вас страдает N сервисов. А как мы видели из примера особенность Entity Service такова, что он связан почти со всеми.
                              И соглашусь и нет. Простые круд сервисы наиболее просты с т.з. тестируемости. Если баг в таком не найден, то архитектура тут — наименьшая из проблем.
                              Но вот да — у вас проблема, да вы ошиблись с балансировкой и масштабированием и да у вас страдает от этого ПРОД. Одно дело если это какой-то изолированный функционал типа регистрации черновика публикации. А другое дело если это и регистрация черновика публикации. И проверка на плагиат публикации и проверка уникальности иллюстраций и даже сервис модерирования. Т.е. разница в масштабах катастрофы разительная просто
                              Вот эту мысль я не совсем понял. Мы рассматриваем архитектуру системы, косяк в конфиге лоадбалансера — из другой оперы. Что вы здесь хотели сказать?
                                0
                                Вот эту мысль я не совсем понял. Мы рассматриваем архитектуру системы, косяк в конфиге лоадбалансера — из другой оперы. Что вы здесь хотели сказать?

                                Попробую другими словами рассказать:

                                image
                                Ну вот вариант 1 с Entity Service для постов и вариант без него. Пердположим при публикации вам нужно выставлять в сущности еще и дату публикации. Это поле должно быть nullable. И по бизнес-логике оно бывает пустым очень редко… в 0,5% случаев. В первом варианте вы добавляете, обновляете создание и обновление. Потом бежите по 4м сервисам и там тоже контракты обновляете (хоть это им не нужно совсем, но они вынуждены, т.к. контракты поменялись). Далее разработчики ошиблись и не сделали в БД это поле nullable. Post service падает в 0.5% случаев. При сохранении черновика публикации — падает. При проверке в двух других случаях — падает, т.к. сохранение не работает. При модерации — падает тоже. Что видит пользователи? Система в целом в этом сценарии не работает.
                                Рассмотрим вариант два. Вы добавляете новое поле только в сервисе черновиков. И все… все остальные если и получают это новое поле, то игнорируют его. Они даже не менялись. В случае ошибки. Падает сохранение черновиков. Проверка — работает, модерация — работает. Вот что в итоге извне видят пользователи. Вторая система должна им явно больше нравиться и для них она выглядит как более надежная. Упала, но не вся…
                            0

                            Статья достаточно очевидная, но аргументация хромает. Многие рассматривают микросервисы только как набор чисто технических "за" или "против". Проблема в том, что микросервисы - это не о "гибкости, простоте, масштабируемости, отказоустойчивости и т.п.". Микросервисы, прежде всего, - о масштабируемости организации. О том, как организовать работу нескольких десятков, сотен и тысяч разрабов. И тут, прежде всего, важным становится независимость разработки и деплоя. Отсюда вывод достаточно простой: entity service имеет смысл рассматривать только когда предметная область и конкретный бизнес уже очень хорошо изучены, а ранее, чем через года 3 это точно не произойдет. В противном случае контракты будут часто меняться и независимая разработка останется только в мечтах.

                              0
                              Микросервисы, прежде всего, — о масштабируемости организации. О том, как организовать работу нескольких десятков, сотен и тысяч разрабов. И тут, прежде всего, важным становится независимость разработки и деплоя.

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

                                Редкий случай когда можно вставить мем с профунктора. Перерисован мной за трудностью поиска оригинала.
                                Картинка
                                image
                              –2
                              Для рассмотрения решения по рефакторингу entity service удобно рассмотреть другой пример.

                              Как же избавиться от Entity Service в случае интернет магазина?
                                0
                                Лишний раз хотелось бы напомнить, что примеры в статье исключительно для понимания сути проблемы. И вовсе не руководство к действию. У них масса изьянов, они не раскрывают лишние аспекты систем, но со своей задачей — донести мысль эффективно справляются. Т.к. максимально упрощены.
                                В реальности интернет магазин может быть сильно разным. Одно дело торговля 10ю плюшевыми игрушками, а другое дело магазин уровня Wieldberies или Яндекс.Маркет. Есть разница. И если в первом случае гарантированно монолит, то во втором случае микросервисы.
                                Как реализовывать микросервисы — ну конкретно в этом случае я бы в первую очередь рассмотрел разбиение на основе subdomains (ссылку на то, что это такое и как с этим работать давал в статье по тексту).
                                И последнее, я тут видео конференций смотрю. И так получилось, что сегодня мне попалось видео, где как раз разбирался такой пример с интренет магазином. Вот ссылка с привязкой ко времени:
                                –2
                                Мало того, что в 2021 году у кого-то здесь ещё остались вопросы по поводу проектирования микросервисов. Судя по данной статье, понимание вопроса до сих пор находится на уровне 5 летней давности. Какие-то Entity, базы данных, фасады :)
                                  0
                                  А не могли бы вы пояснить вашу мысль более развернуто? Как принято 5 лет спустя проектировать системы? Особенно, если это система в закрытом контуре и без облаков?
                                  В целом время само по себе на вопросы не отвечают и до сих пор встречаются люди, которые не слышали про SOLID и DDD. И это нормально, всегда есть что обсудить.
                                    –2
                                    Облака тут даже не при чем. Я имею в виду сам подход к проектированию на основе сущностей. Современная архитектура — это EOA + DDD. Получаем набор слабосвязанных сервисов, которые общаются посредством событий. DDD вообще отвечает на многие вопросы. Ну и конечно, лучше побольше читать англоязычных ресурсов, если хочешь понимать, что происходит.
                                      0
                                      Тогда понятно, что вы имели в виду. Согласен, DDD знать нужно — есть много по нему и русскоязычных источников. И очень хороших, к слову.
                                      По сущностям — ссылка на сайте Майкрософт до сих пор висит, никто ее не удаляет. Это раз. У джавистов в спринге — то же самое. Так что пока ситуация сохраняется и пока эта англоязычная документация висит от лица таких крупных компаний — Entity Service еще не раз многим из нас встретится на практике… ;)
                                        –2
                                        Продукты Майкрософт и Спринг очень плохие учителя)))
                                  0

                                  По-моему Entity Service это из SOA, а не микросервисов. SOA подталкивает к декомпозиции, отталкиваясь от бизнесовых потребностей - сущностей, процессов. Например, в виде сервиса часто встречаются хранилища учётных записей пользователей или документов. Майкрософт приводит набор CRUD операций лишь в качестве примера, где вы там нашли, что это принципиальное ограничение?

                                    0
                                    Да, это из SOA. Суть статьи, конечно, не про Майкрософт вовсе… И даже не про SOA. Скорее про то что получается из Entity Service в микросервисной архитектуре и что получается при слепом следовании примерам из документации.
                                      0
                                      Тут вы подтвердили мои слова из первого комментария :)
                                      Проблема не паттерне, а в его неуместном употреблении.

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

                                  Самое читаемое