Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Вместо хранения только текущего состояния данных в реляционной базе данных сохраните полную серию действий, выполняемых для объекта в хранилище только для добавления. Хранилище выступает в качестве системы записей, которую можно использовать для материализации объектов домена. Такой подход может повысить удобство аудита и производительность записи в сложных системах.
Это важно
Выбор событий — это сложный шаблон, который представляет значительные компромиссы. Он изменяет способ хранения данных, обработки параллелизма, развития схем и состояния запроса. Это затратно для миграции в решение для разработки событий или из него, и после принятия шаблона оно ограничивает будущие решения по проектированию в частях системы, которые используют его. Примите использование event sourcing, когда его преимущества, такие как аудируемость и историческая реконструкция, оправдывают сложность данной схемы. Для большинства систем и большинства частей системы достаточно традиционного управления данными.
Контекст и проблема
Большинство приложений работают с данными. Приложение обычно сохраняет последнее состояние данных в реляционной базе данных и вставляет или обновляет данные по мере необходимости. Например, в традиционной модели создания, чтения, обновления и удаления (CRUD) приложение считывает данные из хранилища, изменяет его и обновляет текущее состояние данных с новыми значениями, как правило, с помощью транзакций, которые блокируют данные.
Подход CRUD прост и быстр для большинства сценариев. Однако в системах с высокой нагрузкой этот подход вызывает проблемы:
Конкуренция на запись: Поскольку обновления требуют цикл чтения-модификации-записи с блокировкой на уровне строк, одновременные записи в одну и ту же сущность снижают производительность и становятся узким местом при высокой нагрузке.
Возможность аудита: Системы CRUD хранят только последнее состояние данных. Если вы не реализуете механизм аудита, который записывает сведения о каждой операции в отдельном журнале, вы потеряете журнал данных.
Решение
Паттерн Event Sourcing определяет подход к обработке операций с данными, которые управляются последовательностью событий. Каждое событие записывается в хранилище только для добавления. Код приложения вызывает события, описывающие каждое действие, выполняемое на объекте. Обычно он отправляет события в очередь, в которой отдельный процесс, обработчик событий, прослушивает очередь и сохраняет события в хранилище событий. Каждое событие представляет логическое изменение объекта, например AddedItemToOrder или OrderCanceled.
События сохраняются в хранилище событий, которое служит системой записей или авторитетным источником данных о текущем состоянии данных. Дополнительные обработчики событий могут прослушивать определенные события и принимать меры по мере необходимости. Например, потребители могут инициировать задачи, которые применяют операции в событиях к другим системам или выполнять другие связанные действия, необходимые для завершения операции. Код приложения, создающий события, отделяется от систем, которые подписываются на события.
Каждая сущность в системе с источником событий имеет собственный поток событий, который является упорядоченной последовательностью событий, записывающих каждое изменение этой сущности. В любой момент приложения могут считывать историю событий. Приложения получают текущее состояние сущности путем воспроизведения всех событий в своем потоке. Этот процесс называется восстановлением. Оно может возникать по запросу, когда приложение обрабатывает запрос.
Приложения обычно реализуют материализованные представления , так как это требует затрат на чтение и воспроизведение событий. Материализованные представления — это проекции хранилища событий только для чтения, оптимизированные для выполнения запросов. Например, система может поддерживать материализованное представление всех заказов клиентов, которые он использует для заполнения пользовательского интерфейса. Когда приложение добавляет новые заказы, добавляет или удаляет элементы в заказе или добавляет сведения о доставке, приложение вызывает события, а обработчик обновляет материализованное представление.
На следующей схеме показан обзор этого шаблона в сочетании с шаблоном сегрегации ответственности запросов команд (CQRS). Слой представления читает из отдельного хранилища только для чтения и отправляет команды в обработчики команд. Обработчики команд извлекают поток событий сущности из хранилища событий, выполняют бизнес-логику и переносят новые события в очередь. Обработчики событий используют события из очереди и записывают события в хранилище событий, обновляют хранилище только для чтения или интегрируются с внешними системами.
Скачать файл Visio архитектуры.
Рабочий процесс
Следующий рабочий процесс соответствует предыдущей схеме:
Слой презентации вызывает объект, который считывается из хранилища только для чтения. Он использует возвращаемые данные для заполнения пользовательского интерфейса.
Слой презентации вызывает обработчики команд для выполнения таких действий, как создание корзины или добавление элемента в корзину.
Обработчик команд загружает сущность, извлекая его поток событий из хранилища событий. Например, он может извлечь все события корзины. Он воспроизводит эти события в отношении сущности, чтобы восстановить её текущее состояние до начала новых действий.
Запускается бизнес-логика и инициируются события. В большинстве реализаций события отправляются в очередь или раздел, чтобы разделить производителей событий и потребителей событий.
Обработчики событий прослушивают определенные события и принимают соответствующее действие для этого обработчика. В этом примере обработчики событий выполняют следующие действия:
Запись событий в хранилище событий
Обновление хранилища только для чтения, оптимизированного для запросов
Интеграция с внешними системами
Преимущества шаблонов
Шаблон хранения событий предоставляет следующие преимущества.
События неизменяемы, и их можно хранить, используя только добавочные операции. Пользовательский интерфейс, рабочий процесс или процесс, инициирующий событие, может продолжаться, и задачи, обрабатывающие события, могут выполняться в фоновом режиме. Пропускная способность записи повышается, особенно на уровне представления, потому что записи только добавляются, избегая конфликтов блокировки на уровне строк, создаваемых системами обновления на месте.
События — это простые объекты, описывающие действие, которое происходит вместе с любыми связанными данными, необходимыми для описания действия, представляющего событие. События не имеют прямого отношения к обновлению хранилища данных. Обработчики событий собирают и обрабатывают записанные события, когда обработчик доступен, и система может обрабатывать нагрузку. Используйте события для упрощения реализации и управления.
События представляют определенную ценность для специалиста по работе с доменами, а объектно-реляционная несогласованность может усложнить понимание сложных таблиц базы данных. Таблицы — это искусственные конструкции, представляющие текущее состояние системы, а не события, которые происходят.
Источник событий может помочь предотвратить возникновение конфликтов, вызванных параллельными обновлениями, так как исключает необходимость непосредственного обновления объектов в хранилище данных. Обработчики команд восстанавливают состояние сущности из её потока событий, чтобы перед добавлением новых событий применить бизнес-правила. Таким образом, два обработчика, загружающие одну и ту же сущность одновременно, могут взаимодействовать с одним и тем же состоянием.
Например, каждый обработчик видит пять оставшихся мест, и оба обработчика могут принять резервирование. Хранилища событий решают этот сценарий, используя управление оптимистичной конкуренцией, и отклоняют операцию добавления, если поток изменился с момента его чтения. При отклонении обработчик перезагружает сущность, переоценивает её и повторяет попытку.
Хранилище событий с добавлением предоставляет журнал аудита, который приложения могут использовать для мониторинга действий, совершенных в отношении хранилища данных. Он может повторно создать текущее состояние в виде материализованных представлений или проекций, перепроверив события в любое время, и он может помочь протестировать и отладить систему.
Требование использовать компенсирующие события для отмены изменений может предоставить журнал обратных изменений. Если модель сохраняет только текущее состояние, эта история не существует. Вы также можете использовать список событий для анализа производительности приложения, обнаружения тенденций поведения пользователей и получения других полезных бизнес-сведений.
Обработчики команд вызывают события и задачи выполняют операции в ответ на эти события. Это разделение задач и событий обеспечивает гибкость и расширяемость. Задачи знают о типе события и данных события, но не об операции, которая активирует событие.
Несколько задач могут обрабатывать каждое событие, поэтому они легко интегрируются с другими службами и системами, которые отслеживают только новые события, инициируемые хранилищем событий. Но события в модели источника событий обычно являются низкоуровневыми, поэтому может потребоваться создать конкретные события интеграции.
Подсказка
Источник событий обычно сочетается с шаблоном CQRS , выполняя задачи управления данными в ответ на события и материализируя представления из хранимых событий. Используйте эту комбинацию для независимого масштабирования операций чтения и записи, так как принятие событий с добавлением и оптимизация прогнозов для запросов работают отдельно.
Проблемы и рекомендации
Учитывайте следующие моменты при принятии решения о том, как реализовать этот шаблон.
Проектирование событий: Разрабатывайте события для фиксации бизнес-целей, стоящих за каждым изменением, в дополнение к достигнутому состоянию. Например, в системе резервирования мест событие, фиксирующее, что два места зарезервированы, является более ценным, чем событие, фиксирующее, что оставшиеся места изменены на 42. Первое событие сообщает вам, что произошло. Второе событие сообщает только результирующее состояние. События, ориентированные на состояние, сокращают хранилище событий до журнала изменений, не имеющего значения для бизнеса. События, ориентированные на намерения, предоставляют более подробные проекции, значимые следы аудита и гибкость создания новых моделей чтения из исторических событий без необходимости изменять среду записи.
Итоговая согласованность: Система в конечном итоге согласована только при создании материализованных представлений или создании проекций данных путем повторения событий. Задержка существует между тем, когда приложение обрабатывает запрос и добавляет события в хранилище событий, когда события публикуются и когда потребители обрабатывают события. В течение этого периода новые события, описывающие дальнейшие изменения сущностей, могут поступать в хранилище событий. Убедитесь, что клиенты понимают, что данные в конечном итоге согласованы и что система предназначена для обеспечения конечной согласованности в этих сценариях.
События версионирования: Хранилище событий является постоянным источником информации, поэтому никогда не следует обновлять данные событий. Единственным способом обновления сущности или отмены изменения является добавление компенсирующего события в хранилище событий. Компенсирующее событие — это новое событие, которое изменяет или исправляет эффект предыдущего события. Например,
ReservationCanceledсобытие компенсирует предыдущееSeatsReservedсобытие. Исходное событие остается в потоке, а компенсирующее событие записывает, что оно было отменено.Эта неизменяемость также означает, что если ошибка создает неправильные события, эти события сохраняются в хранилище. Исправление ошибки в коде приложения не исправляет исторические события, поэтому может потребоваться также компенсирующие события или upcasters для обработки плохих данных во время воспроизведения. Если схема (а не данные) сохраненных событий должна измениться, возможно, во время миграции может быть трудно объединить существующие события в хранилище с новой версией.
Вы можете использовать следующие стратегии по отдельности или в сочетании:
Толерантная десериализация: Проектируйте потребителей событий так, чтобы они игнорировали неизвестные поля и использовали значения по умолчанию для отсутствующих полей. Этот подход обрабатывает аддитивные, неразрывные изменения, например добавление необязательного поля, не требуя преобразования хранимых событий.
Управление версиями событий: Включите идентификатор версии в каждое событие в виде метаданных в конверте события или в составе имени типа события. Потребители используют версию для выбора соответствующей логики обработки.
Upcasting: Регистрируйте функции преобразования, которые преобразуют старые схемы событий в текущую схему во время десериализации. Вы можете объединить upcasters в цепочку так, чтобы код приложения обрабатывал только последнюю версию. Сохраненные события остаются неизменными, что сохраняет неизменяемость.
Миграция на месте: Перепишите исторические события по новой схеме прямо в хранилище событий. Этот подход нарушает неизменяемость и должен быть последним средством, потому что он подрывает след аудита.
Порядок событий: Приложения с несколькими потоками и несколько экземпляров приложений могут хранить события в хранилище событий. Согласованность событий в хранилище событий и порядок событий, влияющих на текущее состояние конкретной сущности, имеет решающее значение. Добавление метки времени в каждое событие может помочь избежать проблем. Еще одна распространенная практика заключается в том, чтобы зафиксировать каждое событие, которое приводит к запросу с добавочным идентификатором. Если два действия пытаются добавить события для одной сущности одновременно, хранилище событий может отклонить событие, которое соответствует идентификатору имеющейся сущности и идентификатору события.
Запросы событий: Нет стандартного подхода или существующих механизмов, таких как запросы SQL, для чтения событий для получения информации. Единственными данными, которые можно извлечь, является поток событий с помощью идентификатора события в качестве критерия. Идентификатор события обычно сопоставляется с отдельными сущностями. Текущее состояние сущности можно определить только путем повторения всех событий, связанных с ним, в исходном состоянии этой сущности.
Параметры хранилища событий: Хранилище событий может быть специально созданной базой данных, предназначенной для потоков событий только для добавления или реляционной базы данных общего назначения или документа с таблицей только для добавления.
Специально созданные хранилища событий обеспечивают встроенную поддержку таких задач, как чтение потока по сущности, оптимистическая конкуренция и моментальные снимки.
Реляционные базы данных знакомы и широко доступны, но требуют реализации этой функциональности самостоятельно.
Так как каждая сущность имеет собственный независимый поток событий, хранилища событий естественно разделяются по идентификатору сущности, что упрощает горизонтальное масштабирование или шардинг при необходимости.
Это важно
Не путайте хранилище событий с брокером сообщений eventstream. Брокеры сообщений, такие как Apache Kafka, обычно не имеют запросов потока сущностей и оптимистичного параллелизма. Они хорошо работают в качестве слоя распространения для распространения событий на проекции и внешних потребителей, но они не являются заменой для хранилища событий.
Повторное создание состояния сущности: Длина каждого потока событий влияет на управление системой и обновление системы. Если потоки большие, воспроизведение каждого события для восстановления сущности становится дорогостоящим как по времени, так и с вычислительными ресурсами. Чтобы снизить эту стоимость, создавайте моментальные снимки с определенным интервалом, например, каждые N событий. Моментальный снимок — это сериализованное представление состояния сущности в определенной точке в его потоке событий. Чтобы восстановить сущность, загрузите последний моментальный снимок и воспроизводите только события, которые происходят после него, а не воспроизводите весь поток с самого начала. При выборе частоты моментальных снимков сбалансируйте затраты на хранение моментальных снимков с учетом времени, сэкономленного во время восстановления.
Замечание
Моментальные снимки — это оптимизация, а не замена потока событий. Поток событий остается источником истины, и вы можете создавать моментальные снимки из него в любое время.
Обработка конфликтов: Элемент управления оптимистичным параллелизмом предотвращает конфликтующие записи в один поток событий, но приложение должно по-прежнему обрабатывать конфликты, охватывающие несколько сущностей. Например, событие, указывающее на сокращение запасов, может прибыть в хранилище данных, пока клиент помещает заказ на этот элемент. Разработка системы для согласования этих ситуаций, таких как консультирование клиента или создание обратного заказа.
Требования к идемпотентности: Доставка событий потребителям обычно происходит как минимум один раз, поэтому потребители могут получать одно и то же событие более одного раза. Обработчики событий должны быть идемпотентными, поэтому обработка повторяющегося события не изменяет результат. Например, если несколько экземпляров процесса потребителя обрабатывают события резервирования мест для поддержания количества доступных мест, дублированное событие резервирования должно привести только к одному уменьшению количества доступных мест. Без идемпотентности проекции отклоняются от потока событий, и побочные эффекты, такие как платежи или уведомления, срабатывают несколько раз. Отслеживайте последний обработанный номер последовательности событий для каждого потребителя и пропускайте дубликаты, или проектируйте изменения состояния, которые по своей сути безопасны для повторения.
Циклическая логика: Помните о сценариях, в которых обработка одного события требует создания одного или нескольких новых событий. Эта последовательность может привести к бесконечному циклу.
Тестирование: Определенный стиль тестирования лучше всего подходит для систем, ориентированных на события. Настройте прошлые события, выполните команду и проверьте новые события, которые были созданы. Этот given-when-then подход позволяет тестировать бизнес-логику без использования баз данных, очередей или проекций. Но вам также нужны тесты интеграции для проекций, поведения идемпотентности и путей эволюции схемы, что добавляет область тестирования по сравнению с системами CRUD.
Персональные данные и соблюдение нормативных требований: Природа хранилища событий, которая предполагает только добавление и является неизменяемой, противоречит требованиям законодательства о защите данных, которые предусматривают удаление персональных данных, таких как законы о «праве на забвение»." Удаление событий серьезно нарушает целостность потока, поэтому учитывайте этот аспект напряженности с самого начала.
Распространенный подход — хранить персональные данные за пределами хранилища событий и ссылаться на него по идентификатору в событиях. Такой подход позволяет удаляться независимо, не влияя на поток событий.
Если вы не можете отделять персональные данные от событий, используйте крипто-измельчение. Шифруйте персональные данные в событиях, используя персональный ключ для каждого субъекта. Удалите ключ, чтобы сделать данные невосстановимыми, оставив структуру событий нетронутой. Этот подход добавляет затраты на шифрование для каждого чтения и записи и требует надежного управления ключами.
Когда следует использовать этот шаблон
Используйте этот шаблон, когда:
Вы хотите записать намерение, назначение или причину в данных. Например, можно записать изменения в сущность клиента в виде ряда определённых типов событий, таких как Смена адреса, Закрытие счета или Смерть.
Необходимо свести к минимуму или полностью избежать конфликтующих обновлений данных.
Вы хотите записать события, которые происходят, чтобы воспроизвести их для восстановления состояния системы, отменить изменения или сохранить историю и аудиторский журнал. Например, если задача состоит из нескольких шагов, может потребоваться выполнить действия, чтобы восстановить обновления, а затем воспроизвести некоторые шаги, чтобы вернуть данные в согласованное состояние.
Приложение уже использует события в качестве естественной функции своей работы, и для получения событий требуется мало дополнительных усилий по разработке или реализации.
Необходимо отделить процесс ввода или обновления данных от задач, необходимых для применения этих действий. Это изменение может быть для повышения производительности пользовательского интерфейса или распространения событий другим прослушивателям, которые действуют при возникновении событий. Например, можно интегрировать систему расчёта заработной платы с веб-сайтом для подачи отчётов о расходах. Веб-сайт и система заработной платы обрабатывают события, вызываемые хранилищем событий в ответ на обновление данных на веб-сайте.
Вы хотите иметь гибкость для изменения формата материализованных моделей и данных сущностей, если требования изменяются, или при использовании CQRS и необходимости адаптировать модель считывания или виды, предоставляющие данные.
При обновлении модели чтения используется CQRS и итоговая согласованность, а также восстановление сущностей и данных из потока событий приводит к приемлемому сокращению производительности.
Этот шаблон может быть не подходит, если:
Системы имеют простые операции CRUD, которые не требуют возможности аудита, перепроигрывания или исторической реконструкции состояния. Операционные издержки хранилища событий не оправданы, если единственным требованием является чтение и запись текущего состояния.
Прототипы, минимальные жизнеспособные продукты (MVPs) или системы имеют короткий срок жизни. Предварительные инвестиции в проектирование событий, стратегию эволюции схемы и инфраструктуру проекций редко дают доход в этих сценариях.
Системам требуется согласованность и обновления в режиме реального времени для отображения данных. В конечном итоге согласованность между хранилищем событий и проекциями связана с источниками событий.
Домены, в которых данные преимущественно статические или используются в качестве ссылок, например таблицы подстановки или каталоги. Этот тип данных редко изменяется и не использует журнал изменений.
Teams не имеют опыта работы с событийно-ориентированными архитектурами. Выбор событий изменяет способ тестирования, отладки и эксплуатации системы. Принятие его без базовых знаний повышает риск антипаттернов, исправление которых дорогостоящее.
Подсказка
Выбор событий не должен быть решением всех или ничего для всей системы. Примените его выборочно к тем частям вашей системы, которым это принесет наибольшую пользу, например, к реестру платежей или процессу обработки заказов. Используйте традиционное CRUD для частей, если сложность не оправдана, например управление профилями пользователей или конфигурация приложений.
Проектирование рабочей нагрузки
Оцените, как использовать шаблон Event Sourcing в проектировании рабочей нагрузки для решения целей и принципов, описанных в столпах платформы Azure Well-Architected Framework. В следующей таблице приведены рекомендации по использованию этого шаблона для целей каждого компонента.
| Столп | Как этот шаблон поддерживает цели основных компонентов |
|---|---|
| Решения по проектированию надежности помогают рабочей нагрузке стать устойчивой к сбоям и гарантировать, что она восстанавливается до полнофункционального состояния после сбоя. | Этот шаблон может упростить восстановление состояния, если необходимо восстановить хранилища состояний, так как вы записываете историю изменений в сложных бизнес-процессах. - Секционирование данных - RE:09 Аварийное восстановление |
| Эффективность производительности помогает рабочей нагрузке эффективно соответствовать требованиям путем оптимизации масштабирования, данных и кода. | Этот шаблон, как правило, в сочетании с CQRS, корректным проектированием домена и стратегическим применением моментальных снимков, может повысить производительность рабочей нагрузки благодаря атомарным операциям только добавления и избежанию блокировки базы данных при записях и чтениях. - Производительность данных PE:08 |
Если этот шаблон вводит компромиссы внутри столпа, рассмотрите их против целей других столпов.
Пример
Система управления конференциями должна отслеживать количество завершенных резервирований для конференции. Отслеживая этот номер, он может проверить наличие доступных мест, когда потенциальный участник пытается забронировать. Система может хранить общее количество заказов на конференцию по крайней мере двумя способами:
Система может хранить сведения о общем количестве резервирования как отдельной сущности в базе данных, содержащей сведения о резервировании. По мере того как участники делают или отменяют резервирование, система увеличивает или уменьшает это число. Этот подход прост в теории, но это может привести к проблемам масштабируемости, если большое количество участников пытается забронировать места в течение короткого периода времени. Например, этот всплеск обычно происходит в последний день до закрытия периода резервирования.
Система может хранить сведения о резервировании и отменах в качестве событий, проводимых в магазине событий. Он вычисляет количество доступных мест путем повторения этих событий. Этот подход может быть более масштабируемым из-за неизменяемости событий. Система должна считывать только данные из хранилища событий или добавлять данные в хранилище событий. Она никогда не изменяет сведения о событиях, связанных с бронированием и отменами.
На следующей схеме показано, как можно использовать ресурсы событий для реализации подсистемы резервирования мест системы управления конференциями.
Скачать файл Visio архитектуры.
Рабочий процесс
Следующий рабочий процесс соответствует предыдущей схеме:
Пользовательский интерфейс выдает команду для резервирования мест для двух участников. Отдельный обработчик команд обрабатывает команду. Обработчик команд — это часть логики, которая отделяется от пользовательского интерфейса и отвечает за обработку запросов, размещенных как команды.
Система создает сущность, содержащую сведения обо всех резервированиях для конференции путем повтора событий, описывающих резервирование и отмены. Эта сущность вызывается
SeatAvailability, и она содержится в модели домена, которая предоставляет методы для запроса и изменения данных в сущности.Подсказка
Рассмотрите такие оптимизации, как моментальные снимки, чтобы не было необходимости воспроизводить полный список событий для получения текущего состояния объекта. Снимки также держат кэшированную копию сущности в оперативной памяти.
Обработчик команд вызывает метод, предоставляемый моделью домена для резервирования.
Сущность
SeatAvailabilityинициирует событие, в котором содержится количество зарезервированных мест. При следующем применении сущности событий все резервирования используются для вычисления количества оставшихся мест.Система добавляет новое событие в список событий в хранилище событий.
Если пользователь отменяет место, система следует аналогичному процессу, но обработчик команд выдает команду, которая создает событие отмены места и добавляет его в хранилище событий.
Система может предоставить полную историю или аудиторский след бронирования и отмены конференции, используя хранилище событий. События в хранилище событий являются точной записью. Вам не нужно сохранять сущности другим способом, так как система может легко воспроизводить события и восстанавливать состояние в любой момент времени.
Следующий шаг
- Шаблон CQRS: хранилище записи, предоставляющее постоянный источник информации для реализации CQRS, обычно основано на реализации шаблона источника событий. Шаблон отделяет операции, которые считывают данные в приложении от операций, которые обновляют данные с помощью отдельных интерфейсов.
Ресурсы сообщества
Событийное моделирование, под авторством Мартина Фаулера: оригинальное описание шаблона 2005 года, введшего базовые термины.
Документы CQRS (PDF), Грег Янг: исчерпывающий ресурс об источниках событий и CQRS от специалиста, который формализировал оба шаблона.
Связанные ресурсы
При реализации этого шаблона также могут быть важны следующие шаблоны и рекомендации.
Шаблон материализованного представления: хранилище данных, используемое в системе источников событий, обычно не подходит для эффективного запроса. Вместо этого распространенный подход заключается в создании предварительно заполненных представлений данных через регулярные интервалы или при изменении данных.
Шаблон компенсирующей транзакции: система не обновляет существующие данные в хранилище источников событий. Вместо этого он добавляет новые записи, которые перемещают состояние сущностей в новые значения. Чтобы отменить изменение, он использует компенсирующие записи, так как он не может отменить предыдущее изменение. В статье о шаблоне компенсирующих транзакций описывается отмена работы, выполняемой предыдущей операцией.
Анализ домена для микрослужб: в системах, использующих разработку на основе домена (DDD), сущность, которая владеет потоком событий, обычно является агрегированной, границой согласованности, которая получает команды, применяет бизнес-правила и выдает события.