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

За лёгкую масштабируемость приходится платить десятками часов проектирования. Чтобы удачно разложить концепцию приложения на части, требуется глубокое погружение — до уровня, где под словом «дизайн» подразумевают не шрифты с иконками и даже не UX. Как понимаете, глубоководные экспедиции в пучину архитектуры окупаются далеко не всегда.

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

Как пользоваться вопросами

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

Каждое «да» — это плюсик в пользу микросервисной архитектуры. Если накопилось много «нет», возможно, вы закладываете в систему необоснованную сложность.

Неравномерное развитие

Части системы развиваются с разной скоростью и/или в разных направлениях?

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

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

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

Нет общих требований к масштабируемости

Характеристики нагрузки и/или пропускной способности частей системы сильно отличаются? У компонентов разные требования к масштабируемости?

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

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

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

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

Требуется повышенная отказоустойчивость

Нужна ли улучшенная защита от конкретного типа сбоев?

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

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

Применяется паттерн «Фасад»

Нужно ли упростить взаимодействие с внешними зависимостями?

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

Микросервисы могут работать как уровень косвенности, ограждающий систему от vendor lock. Вместо того чтобы напрямую вызывать зависимость, мы можем поместить слой абстракции, который контролируем, между основным приложением и зависимостью. Кроме того, мы можем построить этот слой так, чтобы он был прост для потребления нашим приложением, скрывая при этом сложность зависимости.

Если в будущем ситуация изменится и придётся мигрировать, изменения ограничатся только фасадом, без большого рефакторинга.

Используются различные сущности

Приложение обращается к разным сущностям, а также запрашивает данные у бэкенда (а то и нескольких)?

В этом случае лучше разделять сущности по микросервисам.

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

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

Требуется независимый жизненный цикл

Одному или нескольким модулям требуется фиксация кода в производственном потоке?

Если модулю нужен полностью независимый жизненный цикл, он должен быть микросервисом: собственный репозиторий кода, CI/CD конвейер и так далее.

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

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

Но отдельный микросервис может иметь собственный конвейер развёртывания. Такой подход позволяет быстрее выполнять итерации.

Итог

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

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

Если у вас есть своя методика выбора между микросервисами и монолитом, расскажите о ней в комментариях.

Комментарии (14)


  1. rinat_crone
    01.07.2022 10:04
    +11

    В подобных статьях микросервисы часто рассматриваются в контексте получения данных, а контекст записи как-то за скобками остается. А ведь это основное, с чем надо определиться — готовы ли вы к распределенной транзакции? Понимаете ли, чем плох 2PC? Умеете ли в идемпотентность обработки запросов? Какой брокер сообщений будете использовать и почему? Как в Event Sourcing (а вы к нему прийдете, ответив на все вопросы выше) будете реализовывать атомарность выполнения записи в локальную БД и в брокер?

    Не ответив, как минимум, на эти вопросы вы получите не микросервисы, а распределённый монолит.


    1. shai_hulud
      01.07.2022 10:52
      +2

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


      1. rinat_crone
        01.07.2022 10:56
        +3

        Под распределенной транзакцией, Вы, видимо 2PC понимаете. А моя речь как раз про то, что так «в лоб» дизайнить нельзя и нужно использовать другие реализации транзакционности, поскольку 2PC совсем не масштабируется.


        1. shai_hulud
          01.07.2022 11:12
          +1

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

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


          1. rinat_crone
            01.07.2022 11:22
            +3

            Извините, но всё же никто не имеет в виду 2PC, когда в контексте микросервисов говорят о распределённой транзакции. По крайней мере среди тех, с кем мне доводится общаться по этому поводу.

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

            У Вас либо идеальный мир, либо очень простые проекты. Не видел ещё ни одной распределенной системы, даже самой грамотно спроектированной, где хоть в каком-то виде не надо было бы атомарно выполнять действия в разных микросервисах.


            1. shai_hulud
              01.07.2022 14:12
              +1

              У Вас либо идеальный мир, либо очень простые проекты.

              У меня специфичный опыт, это геймдев. И да, простые проекты, ведь спроектировать наиболее простой проект это и есть задача разработчика. Спроектировать сложный проект может любой.

              Не видел ещё ни одной распределенной системы, даже самой грамотно спроектированной, где хоть в каком-то виде не надо было бы атомарно выполнять действия в разных микросервисах.

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


    1. Valdemarskyi
      01.07.2022 13:02
      +1

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


    1. GrogeM
      02.07.2022 22:13

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


  1. alexzaides
    01.07.2022 13:04
    +3

    К сожалению, сейчас мало кто задумывается над вопросом, когда и как нужно использовать микросервисы. Мода - страшная штука. Микросервисы и скрам в тренде - значит в 99% случаях все будут работать по скраму и писать микросервисы. Даже если имеются отлаженные бизнес процессы, которые не менялись сто лет и ещё столько же не поменяются. Даже если разработка без понимания бизнес процесса в целом превращается в танцы на граблях, а половина кода и проблем в совершенно искусственно разделённых сервисах относится именно к взаимодействию сервисов - всё равно - монолит это фу-фу, каменный век и сейчас никто так не пишет, а думать дальше текущего или, в крайнем случае следующего одного-двух спринтов - это не по методологии.


  1. piratarusso
    01.07.2022 14:08
    +1

    Монолит всегда лучше. Для базовой и критической части систем монолит всегда лучше. Это если хватает ресурсов на разработку. И в обслуживании дешевле.


    1. demimurych
      02.07.2022 21:57

      В вас говорит небольшой опыт и излишняя горячесть.

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


      1. alexzaides
        02.07.2022 22:22

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


  1. maslyaev
    02.07.2022 21:59

    Эта статья про сервисы или микросервисы? Это ведь совсем две разные вещи. Упоминаемая для примера система обработки заказов - никакой не микросервис, а полноценный сервис, который сослепу можно посчитать монолитом. А вот паттерн "Фасад" (он же паттерн "Передаст") - типичный кейс микросервиса. Так про что статья?


    1. alexzaides
      02.07.2022 22:18

      А в какой момент микросервис превращается просто в сервис? и в какой момент система сервисов превращается в зоопарк общающихся между собой монолитов?