Когда действительно пора делать микросервисы

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

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

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

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

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

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

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

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

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

233400d9e9c563f0026f1564d0cc308d.jpg

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Итог

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

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

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

© Habrahabr.ru