Оригинал: Top 10 Microservices Design Patterns you should know
Перевод для канала Мы ж программист
Что такое микросервис?
В качестве напоминания и перед тем, как перейти к подробному описанию паттернов проектирования, давайте рассмотрим, что такое микросервис и какие проблемы он создает.
Микросервис – это небольшой, независимо развертываемый компонент большого приложения, который фокусируется на определенной функциональности. Каждый микросервис выполняет свой собственный процесс, взаимодействует с другими сервисами, как правило, с помощью API, и спроектирован как слабосвязанный, что позволяет легче масштабировать, разрабатывать и обслуживать его.

Микросервисы имеют следующие основные преимущества перед монолитными приложениями:
- Масштабируемость: Отдельные микросервисы могут масштабироваться независимо друг от друга в зависимости от потребностей, оптимизируя использование ресурсов.
- Гибкость: Разные микросервисы можно разрабатывать, тестировать, развертывать и поддерживать, используя разные технологии.
- Ускоренная разработка: Небольшие сфокусированные команды могут одновременно работать над отдельными микросервисами, ускоряя циклы разработки и время выпуска релизов.
- Устойчивость: Сбои в одном микросервисе изолированы и с меньшей вероятностью повлияют на всю систему, что повышает общую надежность.
- Более простое обслуживание: Маленькие кодовые базы для каждого микросервиса легче понять, модифицировать и отладить, что снижает технический долг.
- Гибкость при аутсорсинге: Защита интеллектуальной собственности может стать проблемой при передаче бизнес-функций сторонним партнерам. Архитектура микросервисов может помочь, изолируя компоненты, специфичные для партнера, обеспечивая безопасность основных сервисов и не затрагивая их.
С другой стороны, это сопряжено с определенными трудностями:
- Сложность: Разработка и поддержка приложения на базе микросервисов обычно требует больше усилий, чем монолитный подход. Для каждого сервиса требуется своя кодовая база, тестирование, конвейер развертывания и документация.
- Межсервисная коммуникация: Микросервисы полагаются на сетевые коммуникации, что может привести к задержкам, сбоям и сложностям в обработке межсервисных коммуникаций.
- Управление данными: Распределенное управление данными может быть сложным, поскольку каждый микросервис может иметь свою собственную базу данных, что приводит к проблемам с согласованностью, синхронизацией данных и транзакциями.
- Накладные расходы на развертывание: Управление развертыванием, версионированием и масштабированием множества микросервисов может потребовать сложных инструментов оркестровки и автоматизации, таких как Kubernetes.
- Безопасность: Каждый микросервис может создавать новые потенциальные уязвимости, увеличивая поверхность атаки и требуя пристального внимания к методам обеспечения безопасности.
Следующие шаблоны проектирования в основном предназначены для решения некоторых проблем микросервисов:
- Шаблон Database Per Service
- Шаблон API Gateway
- Шаблон Backend for Frontend
- Command Query Responsibility Segregation (CQRS)
- Шаблон Event Sourcing
- Шаблон Saga
- Шаблон Sidecar
- Шаблон Circuit Breaker
- Anti-Corruption Layer
- Шаблон Aggregator
Давайте рассмотрим их более подробно.
Шаблон Database Per Service
Паттерн Database per Service – это подход к проектированию в архитектуре микросервисов, при котором каждый микросервис имеет свою собственную выделенную базу данных. Доступ к каждой базе данных осуществляется только через собственный API микросервиса. База данных сервиса фактически является частью реализации этого сервиса. К ней не могут напрямую обращаться другие сервисы.

Если выбрана реляционная база данных, есть 3 способа сохранить данные закрытыми для других баз данных:
- Индивидуальные таблицы для каждого сервиса: Каждый сервис владеет набором таблиц, доступ к которым должен быть только у этого сервиса.
- Схема для каждого сервиса: Каждый сервис имеет схему базы данных, которая индивидуальна для этого сервиса.
- Сервер БД для каждого сервиса: Каждый сервис имеет свой собственный сервер базы данных.
Вот основные преимущества использования этого шаблона:
- Слабая связанность: Сервисы меньше зависят друг от друга, что делает систему более модульной.
- Гибкость технологий: Команды могут выбрать оптимальную технологию баз данных, подобрать подходящий размер базы данных для конкретных требований к сервису для каждого микросервиса.
Шаблон проектирования всегда имеет компромиссы, вот некоторые проблемы, которые не решаются с помощью этого шаблона:
- Сложность: Управление несколькими базами данных, включая резервное копирование, восстановление и масштабирование, добавляет сложности системе.
- Межсервисные запросы: Сложно реализовать запросы к данным, распределенным по нескольким базам данных. Для решения этой проблемы можно использовать API Gateway или паттерн Aggregator.
- Согласованность данных: Поддержание согласованности данных в базах данных различных сервисов требует тщательного проектирования и часто предполагает использование других паттернов, таких как Event sourcing или Saga.
Шаблон API Gateway
Паттерн API Gateway – это подход к проектированию в архитектуре микросервисов, при котором единая точка входа (API-шлюз) обрабатывает все запросы клиентов. Шлюз API выступает в качестве посредника между клиентами и микросервисами, направляя запросы к соответствующему сервису, агрегируя ответы и часто управляя такими сквозными задачами, как аутентификация, балансировка нагрузки, протоколирование и ограничение скорости.

Вот основные преимущества использования API-шлюза в микросервисной архитектуре:
- Упрощенное взаимодействие с клиентами: Клиенты взаимодействуют с единым, унифицированным API вместо того, чтобы напрямую работать с несколькими микросервисами.
- Централизованное управление: Сквозные проблемы решаются в одном месте, что позволяет сократить дублирование кода в разных сервисах.
- Повышенная безопасность: Шлюз API может применять политики безопасности и контроль доступа, защищая базовые микросервисы.
Вот основные недостатки:
- Единая точка отказа: Если API-шлюз выйдет из строя, вся система может стать недоступной, поэтому он должен быть высокодоступным и отказоустойчивым.
- Нагрузка на производительность: Шлюз может вносить задержки и стать узким местом, если его не оптимизировать должным образом при масштабировании.
Шаблон Backend for Frontend
Паттерн Backend for Frontend (BFF) – это подход к проектированию, при котором для каждого конкретного фронтенда или клиентского приложения, такого как веб-приложение, мобильное приложение или приложение для настольных компьютеров, создается специальный бэкенд-сервис. Каждый BFF предназначен для удовлетворения специфических потребностей соответствующего фронтенда, обрабатывая данные, преобразовывая их и взаимодействуя с базовыми микросервисами или API. Паттерн BFF лучше всего использовать в ситуациях, когда существует несколько внешних приложений с различными требованиями.

Вот преимущества такого шаблона:
- Оптимизированная связь с фронтендами: Фронтенды получают именно то, что им нужно, что приводит к ускорению загрузки и улучшению пользовательского опыта.
- Снижение сложности для фронтендов: Фронтенд упрощается, так как BFF управляет сложной агрегацией, трансформацией и бизнес-логикой данных.
- Независимая эволюция: Каждый фронтенд и соответствующий ему BFF могут развиваться независимо друг от друга, что обеспечивает большую гибкость в разработке.
Однако у этого шаблона есть и недостатки:
- Сложность: Ведение отдельных BFF для разных фронтендов увеличивает сложность разработки и обслуживания.
- Потенциальное дублирование: Общая функциональность BFF может привести к дублированию кода, если не управлять им должным образом.
- Согласованность: Обеспечение согласованного поведения различных BFF может быть сложной задачей, особенно в больших системах.
Command Query Responsibility Segregation (CQRS)
Шаблон CQRS – это подход к проектированию, при котором обязанности по чтению данных (запросы) и записи данных (команды) разделяются на разные модели или сервисы. Разделение обязанностей позволяет каждой модели быть приспособленной для выполнения своей конкретной функции:
- Модель команд: Может быть оптимизирована для обработки сложной бизнес-логики и изменений состояния.
- Модель запросов: Может быть оптимизирована для эффективного поиска и представления данных, часто с использованием денормализованных представлений или кэшей.
Связь между сервисами чтения и записи может осуществляться несколькими способами, например, очередями сообщений или с помощью паттерна Event sourcing, описанного ниже.

Вот основные преимущества шаблона CQRS:
- Оптимизация производительности: Каждая модель может быть оптимизирована для выполнения конкретных операций, что повышает общую производительность системы.
- Масштабируемость: Операции чтения и записи можно масштабировать независимо друг от друга, что повышает эффективность использования ресурсов.
- Удобство обслуживания: Благодаря разделению обязанностей команд и запросов кодовая база становится более организованной, ее легче понять и модифицировать.
Вот проблемы, связанные с этим шаблоном:
- Сложность: Необходимость управлять и синхронизировать отдельные модели для команд и запросов усложняет систему.
- Согласованность данных: Обеспечение согласованности между моделями команд и запросов, особенно в распределенных системах, где обновления данных могут распространяться не сразу, может оказаться сложной задачей.
- Синхронизация данных: Синхронизация моделей чтения и записи может быть сложной задачей, особенно при больших объемах данных или сложных преобразованиях. Справиться с этой сложной задачей помогут такие методы, как event sourcing или очереди сообщений.
Шаблон Event Sourcing
Шаблон Event Sourcing фиксирует изменения состояния в виде последовательности событий, которые хранятся в хранилище событий вместо непосредственного сохранения текущего состояния. Это хранилище событий действует как брокер сообщений, позволяя сервисам подписываться на события через API. Когда сервис регистрирует событие, оно отправляется всем заинтересованным подписчикам. Чтобы восстановить текущее состояние, все события в хранилище событий воспроизводятся по порядку. Последний процесс может быть оптимизирован с помощью моментальных снимков, чтобы не воспроизводить все события, а только последние.

Вот основные преимущества схемы поиска источников событий:
- Аудиторский след: Предоставляет полную историю изменений, что полезно для аудита, отладки и понимания того, как система развивалась с течением времени.
- Масштабируемость: Благодаря хранению только событий, операции записи можно легко масштабировать. Это позволяет системе обрабатывать большой объем записей от нескольких потребителей без проблем с производительностью.
- Эволюционность: Легкое добавление новой функциональности путем введения новых типов событий, поскольку бизнес-логика обработки событий отделена от хранилища событий.
Недостатки:
- Сложность: Необходимость управлять потоками событий и восстанавливать состояние может быть сложнее, чем при традиционном подходе, кроме того, для освоения этой практики необходимо пройти обучение.
- Более высокие требования к хранению: Event Sourcing обычно требует большего объема памяти, чем традиционные методы, поскольку все события должны храниться и сохраняться для исторических целей.
- Сложные запросы: Запрос данных о событиях может быть более сложным, чем при использовании традиционных баз данных, поскольку текущее состояние должно быть восстановлено на основе событий.
Шаблон Saga
Шаблон Saga используется в распределенных системах для управления длительными бизнес-транзакциями в нескольких микросервисах или базах данных. Для этого он разбивает транзакцию на последовательность локальных транзакций, каждая из которых обновляет базу данных и запускает следующий шаг по событию. Если транзакция завершилась неудачно, Saga запускает компенсирующие транзакции, чтобы отменить изменения, сделанные на предыдущих шагах.

Вот наглядный пример паттерна Saga с компенсирующими транзакциями:

Саги можно координировать двумя способами:
Хореография: Каждый сервис прослушивает события и запускает следующий шаг в саге. Это децентрализованный подход, при котором сервисы напрямую общаются друг с другом.

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

Вот основные преимущества шаблона Saga:
- Согласованность данных: Позволяет приложению поддерживать согласованность данных в нескольких сервисах.
- Повышенная устойчивость: Разбивая транзакции на более мелкие, независимые шаги с компенсирующими действиями, паттерн Saga повышает способность системы справляться со сбоями без потери согласованности данных.
Но у него есть и недостатки:
- Сложность: реализация паттерна Saga может усложнить систему, особенно при управлении компенсирующими транзакциями и обеспечении правильной координации всех шагов.
- Отсутствие автоматического отката: В отличие от ACID-транзакций, сага не имеет автоматического отката, поэтому разработчики должны проектировать компенсирующие транзакции для явной отмены изменений, сделанных ранее в саге.
- Отсутствие изоляции: Отсутствие изоляции («I» в ACID) в сагах повышает риск аномалий данных во время одновременного выполнения саг.
Шаблон Sidecar
Паттерн Sidecar предполагает развертывание вспомогательного сервиса (sidecar) вместе с основным сервисом приложения в одной и той же среде, например в контейнере или поде. Этот sidecar выполняет вспомогательные задачи, такие, как ведение журналов, мониторинг или обеспечение безопасности, расширяя функциональность основного сервиса без изменения его кода. Этот паттерн способствует модульности и масштабируемости за счет перекладывания неосновных обязанностей на sidecar, позволяя основному сервису сосредоточиться на своей основной функциональности.

Прежде чем перейти к рассмотрению плюсов и минусов этого паттерна, давайте посмотрим на некоторые случаи его использования:
- Ведение журналов и мониторинг: Дополнительный сервис может собирать журналы или метрики от основного сервиса и передавать их в централизованные системы для анализа.
- Безопасность: Сторонние сервисы могут управлять функциями безопасности, такими как аутентификация, авторизация и шифрование. Передача этих обязанностей побочной службе позволяет основной службе сосредоточиться на своей бизнес-логике.
Вот основные преимущества этого паттерна:
- Модульность и расширяемость: Паттерн Sidecar позволяет разработчикам легко добавлять или удалять функциональные возможности путем присоединения или отсоединения контейнеров sidecar, что повышает степень повторного использования кода и удобство обслуживания системы, не затрагивая основной сервис.
- Изоляция проблем: Контейнер работает отдельно от основного сервиса, изолируя вспомогательные функции и минимизируя влияние сбоев в работе контейнера.
- Масштабируемость: Благодаря отделению основного сервиса от sidecar каждый компонент может масштабироваться независимо, исходя из своих специфических потребностей, гарантируя, что масштабирование основного сервиса или sidecar не повлияет на другой.
А вот и основные недостатки:
- Повышенная сложность: Добавляет дополнительный уровень сложности, требуя управления и координации нескольких контейнеров, что может увеличить затраты на развертывание и эксплуатацию.
- Потенциальная единая точка отказа: Контейнер sidecar может стать единой точкой отказа, что требует применения таких механизмов обеспечения устойчивости, как резервирование и проверка работоспособности.
- Латентность (задержка): Вносит дополнительные коммуникационные накладные расходы, что может повлиять на производительность, особенно в приложениях, чувствительных к задержкам.
- Синхронизация и координация: Обеспечение надлежащей синхронизации между основной службой и вспомогательной может быть сложной задачей, особенно в динамичных средах.
Шаблон Circuit Breaker
Шаблон Circuit Breaker (автоматический выключатель) – это подход к проектированию, используемый для повышения устойчивости и стабильности распределенных систем путем предотвращения каскадных отказов. Он функционирует подобно электрическому выключателю: когда служба сталкивается с пороговым числом последовательных отказов, выключатель срабатывает, останавливая все запросы к отказавшей службе на время тайм-аута. Во время этого тайм-аута система может восстановиться без дополнительных нагрузок. По истечении тайм-аута автоматический выключатель разрешает ограниченное количество тестовых запросов, чтобы проверить, восстановилась ли служба. В случае успеха нормальная работа возобновляется, если нет – тайм-аут сбрасывается. Этот паттерн помогает управлять доступностью сервисов, предотвращать перегрузку системы и обеспечивать плавную деградацию в средах микросервисов.

Схема автоматического выключателя обычно работает в трех основных состояниях: Закрыто, Открыто и Полуоткрыто. Каждое состояние представляет собой отдельную фазу в управлении взаимодействием между сервисами. Вот объяснение каждого состояния:
- Закрыто: Автоматический выключатель позволяет запросам проходить к сервису. Он отслеживает ответы и отказы. Если количество отказов превышает заданный порог, автоматический выключатель переходит в состояние «Открыто».
- Открыт: Автоматический выключатель не позволяет любым запросам достигать отказавшего сервиса, перенаправляя их на резервный механизм или возвращая ошибку. Это состояние дает сервису время на восстановление после проблем.
- Полуоткрытое состояние: После заданного периода восстановления автоматический выключатель переходит в состояние «Полуоткрыто», в котором он разрешает ограниченное количество запросов, чтобы проверить, восстановился ли сервис. Если эти запросы успешны, автоматический выключатель возвращается в состояние «Закрыто»; в противном случае он снова переходит в состояние «Открыто».

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

Anti-Corruption Layer
Паттерн Anti-Corruption Layer (ACL) – это паттерн проектирования, используемый для предотвращения влияния дизайна и моделей данных внешних систем на внутренний дизайн и модели данных системы. Он действует как барьер или переводчик между двумя системами, гарантируя, что внутренняя система остается изолированной от сложностей и несоответствий внешних систем и не подвержена их влиянию.

Дизайн внутри ACL можно представить таким образом:

Вот основные преимущества шаблона ACL:
- Защита: Защищает внутреннюю систему от внешних изменений и потенциального повреждения.
- Гибкость: Более легкая интеграция с внешними системами за счет управления различиями в моделях данных и протоколах.
- Удобство обслуживания: Упрощает внесение изменений и обновлений во внутреннюю или внешнюю систему, не затрагивая другую.
С другой стороны, вот основные проблемы шаблона ACL:
- Латентность (задержка): Латентность может быть добавлена за счет вызовов, выполняемых между двумя системами.
- Масштабирование: Масштабирование ACL с большим количеством микросервисов или монолитных приложений может стать проблемой для команды разработчиков.
- Дополнительная сложность: создает дополнительную сложность из-за необходимости трансляции и адаптации логики.
Шаблон Aggregator
Шаблон Aggregator – это шаблон проектирования, используемый для объединения данных или ответов из нескольких источников в единый, унифицированный результат. Компонент или служба агрегатора управляет сбором данных из различных источников, координируя процесс получения, объединения и обработки данных.

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