Внедрение Event Sourcing и Cqrs в бессерверных архитектурах
Введение в Event Sourcing и CQRS
Сегрегация событий и ответственности командных запросов (CQRS) стали основополагающими моделями для построения современных распределенных систем. В сочетании с архитектурами без серверов эти шаблоны открывают беспрецедентную масштабируемость, устойчивость и аудитоспособность. Эта статья обеспечивает тщательное исследование внедрения событийных источников и CQRS в безсерверных средах, охватывая основные концепции, стратегии практической реализации, общие подводные камни и лучшие практики в реальном мире.
Источник событий: сохранение изменений как последовательности событий
Event Sourcing — это шаблон сохранения данных, где каждое изменение состояния приложения фиксируется как неизменное событие. Вместо хранения только текущего состояния система записывает хронологический журнал событий. Текущее состояние может быть реконструировано путем повторения этих событий. Этот подход обеспечивает полный контрольный след, позволяет выполнять временные запросы (например, «Какое состояние было в заданную дату?»), и упрощает отладку и соответствие.
В безсерверном контексте хранилище событий должно быть очень прочным, масштабируемым и с низкой задержкой. Общие варианты включают AWS DynamoDB, Azure Cosmos DB или . DynamoDB, с его режимом пропускной способности по требованию, естественным образом вписывается в модели безсерверного выставления счетов и может обрабатывать потоки событий любого объема. Данные о событиях обычно хранятся в таблице с первичным ключом, который включает в себя совокупный идентификатор и номер версии для обеспечения упорядочения и идемпотентности.
Каноническая статья Мартина Фаулера о Источнике Событий (FLT:0) остается окончательным ориентиром для понимания нюансов шаблона.
Структура событий и схема
Каждое событие должно содержать как минимум: тип события, временную метку, совокупный идентификатор, номер версии и полезную нагрузку с изменившимися данными. Использование реестра схем (например, ] Google Cloud Schema Registry или AWS EventBridge Schema Registry ) помогает поддерживать обратную совместимость по мере развития событий.
CQRS: Отделение чтения от письма
CQRS (Command Query Responsibility Segregation) отделяет модели, используемые для обработки команд (записей), от тех, которые используются для обработки запросов (чтений). В безсерверной архитектуре это означает развертывание отдельных функций или служб: обработчики команд пишут, часто добавляя события в хранилище событий, в то время как обработчики запросов читают из оптимизированных моделей чтения - обычно денормализованные таблицы, материализованные представления или поисковые индексы.
Это разделение приносит значительные преимущества: рабочие нагрузки записи остаются наклонными и сосредоточенными на проверке и устойчивости событий, в то время как модели чтения могут быть настроены для быстрого поиска, включая предварительные соединения, агрегации и возможности полнотекстового поиска. Обе стороны общаются через асинхронные механизмы, такие как потоки событий или очереди сообщений (например, AWS SQS, Azure Queue Storage, Google Cloud Tasks).
Оригинальная документация CQRS Грега Янга обеспечивает основополагающий контекст для шаблона.
Комбинирование событийных источников и CQRS в Serverless
При совместном использовании Event Sourcing и CQRS образуют мощный дуэт: команды создают события, хранящиеся в журнале событий, а проекции (или подписчики) асинхронно обновляют прочитанные модели.Безсерверные платформы превосходят эту парадигму, основанную на событиях, потому что они абстрагируют управление инфраструктурой и автоматически масштабируют каждый компонент на основе нагрузки.
Ниже приведен типичный бессерверный поток системы, основанный на событиях:
- Пользовательское действие запускает командную функцию (например, AWS Lambda за API Gateway).
- Командная функция проверяет ввод, производит одно или несколько событий домена и добавляет их в хранилище событий (DynamoDB, Cosmos DB и т. Д.).
- После добавления событий функция публикует сообщение (например, Amazon EventBridge, Azure Event Grid или Google Pub/Sub), указывающее на то, что новые события доступны.
- Функции прогнозирования подписываются на поток событий и обновляют модель чтения (например, денормализованную таблицу DynamoDB, индекс Elasticsearch или кэш, как Redis).
- Функции запроса обслуживают запросы чтения непосредственно из модели чтения, никогда не запрашивая магазин событий.
Эта конструкция обеспечивает оптическую согласованность между сторонами записи и чтения, что является основным компромиссом CQRS. Во многих бизнес-сферах возможная согласованность приемлема и даже желательна, поскольку она позволяет увеличить пропускную способность и снизить задержку считывания.
Пример: Управление заказами электронной коммерции
Рассмотрим систему заказа. Пользователь размещает заказ (команд), который излучает событие . Функция проекции считывает это событие и обновляет модель чтения резюме заказа, которая включает название продукта, количество и текущее состояние. Другая проекция может обновить модель чтения инвентаря. Если пользователь позже запрашивает историю заказа, функция запроса считывает из предварительно построенной модели резюме, избегая дорогостоящих соединений или считываний из магазина необработанных событий.
Внедрение Event Store в базы данных без сервера
Выбор дизайна для магазина событий напрямую влияет на производительность и стоимость. С DynamoDB общий подход заключается в использовании одной таблицы с составным первичным ключом: (ключ раздела) и (ключ раздела). Это позволяет быстро извлекать все события для конкретного агрегата в порядке. Хранение всего потока событий в одном ключе раздела гарантирует, что такие операции, как моментальный снимок (периодические сохранения агрегатного состояния) остаются эффективными.
Для рабочих нагрузок, требующих кросс-агрегированных запросов, рассмотрите возможность использования вторичного индекса по типу события или временной метки.Однако избегайте сканирования всего хранилища событий; такие потребности лучше обслуживаются специализированными моделями чтения.
На Azure Cosmos DB предлагает аналогичные возможности с настраиваемыми уровнями согласованности и автоматической индексацией. Модель поиска событий в Azure Architecture Center обеспечивает руководство, конкретное для этой платформы.
Конкурентность и императивность
Concurrent пишет в один и тот же агрегат, и с ним нужно обращаться осторожно. Использование оптимистического контроля параллелизма (например, условное обновление с проверкой версии в DynamoDB) гарантирует, что только одна команда будет успешно выполняться при увеличении версии. В случае конфликта команда может быть повторно проверена после повторного чтения последних событий. Идемпотенция обеспечивается путем хранения уникального идентификатора (например, идентификатор корреляции) с каждым событием, позволяя обработчику команд обнаруживать дубликаты и изящно их отклонять.
Создание моделей чтения с проекциями
Проекции — это функции, которые потребляют события и обновляют одну или более моделей чтения. В безсерверных системах они лучше всего реализуются как управляемые событиями функции, запускаемые шиной события. Каждая функция проекции должна быть идемпотентной: если событие обрабатывается более одного раза (например, из-за повторного повтора), обновление прочитанной модели должно давать тот же результат.
Общие стратегии построения моделей чтения включают:
- Денормализованные таблицы в DynamoDB или Cosmos DB, которые отражают шаблоны запросов (например, все заказы для пользователя).
- Индексы поиска в Elasticsearch, Amazon OpenSearch или Azure Search для полнотекстовых и граненых запросов.
- Материализированные представления с использованием потоковых фреймворков, таких как AWS Kinesis Data Analytics или Azure Stream Analytics.
- Каши в памяти (например, ElastiCache, Redis) для запросов с ультранизкой задержкой с инвалидизацией на основе TTL.
Чтобы избежать тесной связи, проекции должны быть без состояния и управляться исключительно полезной нагрузкой события. Они могут быть добавлены, удалены или изменены без воздействия на командную сторону.
Обработка событийной последовательности и SAGA
Одной из самых больших проблем в системе CQRS/ES является управление возможной согласованностью и координация многоэтапных бизнес-транзакций. Пользователь может разместить заказ, но модель чтения может не отражать это изменение в течение нескольких сотен миллисекунд. Для синхронных ожиданий пользователя (например, отображение страницы подтверждения) обработчик команд может немедленно вернуть идентификатор события, в то время как фронтенд опросы для обновления модели чтения или подписки на канал WebSocket.
Для многошаговых процессов, требующих распределенных транзакций, предпочтительным решением является паттерн SAGA. Каждый шаг в саге излучает события, а компенсирующие события хранятся в хранилище событий для отмены частично завершенных шагов. Функции без сервера и прочные оркестроры (например, функции AWS Step, функции Azure Durable, рабочие процессы Google) могут надежно реализовывать саги без длительных блокировок.
Обработка ошибок и императивность по масштабу
Безсерверные среды подвержены временным сбоям и дублирующим вызовам. Обработчики событий должны быть разработаны для идемпотентности. Сохранить окно дедупликации (например, с использованием DynamoDB TTL или набора Redis), которое записывает обработанные идентификаторы событий. Если событие снова попадает в окно, оно молча игнорируется.
Когда команда не срабатывает после добавления событий в магазин, события уже были написаны. В таких случаях вам может потребоваться реализовать компенсирующее событие (например, )], чтобы вернуть состояние. Компенсирующее событие хранится так же, как обычное событие, и запускает проекцию, которая отменяет работу.
Также рассмотрите очереди с пустыми буквами (DLQ) для событий, которые неоднократно не обрабатываются. DLQ позволяют проверять и воспроизводить события после исправления проблемы, не теряя данные.
Оптимизация производительности и затрат в бессерверных системах событий
В то время как безсерверные масштабы автоматически, небрежно разработанный источник событий может повлечь за собой высокие затраты. Ключевые области оптимизации включают:
- Обработка матчей: При проектировании событий читайте и записывайте пакетами, чтобы минимизировать запросы к базе данных. DynamoDB может обрабатывать до 25 пунктов одновременно.
- Снимки: Периодически сохраняйте снимки агрегатных состояний, чтобы избежать повторения всего журнала событий на каждом чтении. Снимки хранятся в одной таблице хранилища событий со специальной версией (например, номер версии, которому предшествует «SNAP»).
- Каширование: Кэш часто обращался к данным считываемой модели на уровне приложения (например, с использованием ElastiCache или CloudFront с динамическим контентом).
- Разделение событий: Если использовать систему паб/суб, такую как EventBridge, события разделов по агрегатному типу для управления скоростью вызова для функций проекции.
Пример: Стратегия снимка в DynamoDB
Храните снимок с помощью ключа раздела = aggregateId и сортируйте ключ = «SNAP#
Тестирование и отладка серверных систем, исчерпанных событиями
Тестирование архитектур, основанных на событиях, требует различных стратегий, чем традиционные системы CRUD. Единичные тесты могут проверять, что обработчики команд производят правильные события, заданные входом. Интеграционные тесты должны подтверждать, что проекции правильно обновляют прочитанные модели при публикации событий. Поскольку функции без сервера не имеют состояния, рассмотрите возможность использования локальных эмуляторов (например, AWS SAM local, DynamoDB Local, EventBridge local testing library) для запуска тестов в трубопроводах CI/CD.
Отладка производственных проблем выигрывает от самого журнала событий — вы можете воспроизводить события в среде разработки, чтобы воссоздать точную последовательность, которая привела к ошибке. Инструменты, такие как AWS X-Ray или Azure Monitor , помогают отслеживать вызовы функций в службах.
Обычные подводные камни и как их избежать
- Неправильное моделирование доменов: Не каждый бизнес-домен выигрывает от поиска событий. Если вам нужен простой CRUD без требований аудита, накладные расходы могут быть не оправданы.
- Сверхбольшие события: Хранение больших полезных нагрузок (например, целых документов) в качестве одного события снижает производительность.
- Дрифт следования: Когда прочитанные модели становятся не синхронизированными из-за пропущенных событий или ошибок, вам нужен механизм повторения.Постройте функцию повторения, которая может перерабатывать все события из заданного момента времени.
- Игнорирование эволюции схем: События неизменны, но их схемы меняются. Используйте реестр и версию каждого типа событий. Создайте новые прогнозы для обработки нескольких версий.
- Холодные старты, влияющие на проекции: Проекционные функции, которые вызываются нечасто, могут страдать от задержки холодного старта. Рассмотрим использование предусмотренной параллели для критических проекций или сопоставления событий в меньшее количество вызовов.
Архитектурный пример реального мира
Приложение для финансовой торговли, построенное на AWS Lambda, DynamoDB и EventBridge, внедрило поиск событий для записи каждого торгового заказа. Командные функции обрабатывали заказы на покупку / продажу и издавали события , и . Прогнозы обновили таблицу DynamoDB для портфеля пользователя и кластер Elasticsearch для аналитики рынка в режиме реального времени. Система обрабатывала более 10 000 событий в секунду в часы пик, с доступностью 99,99% и задержкой в секунду для запросов портфеля, благодаря моментальному снимку и эффективному дизайну модели чтения.
Эта команда избежала распространенных ошибок, обеспечив строгую версию схемы событий (с использованием Apache Avro) и внедрив специальный конвейер воспроизведения, который мог бы перестроить все модели чтения с нуля менее чем за 30 минут.
Заключение
Внедрение Event Sourcing и CQRS в бессерверных архитектурах дает командам разработчиков возможность создавать высокомасштабируемые, проверяемые и обслуживаемые системы. Используя полностью управляемые сервисы для хранения событий, маршрутизации сообщений и вычислений, вы можете сосредоточиться на бизнес-логике, в то время как платформа обрабатывает проблемы инфраструктуры. Ключевые факторы успеха включают тщательное моделирование событий, идемпотентные прогнозы, оптимизацию снимков и надежную обработку ошибок. С этими практиками поиск событий и CQRS становятся мощными инструментами для решения сложных бизнес-доменов в облаке.