Как обеспечить синхронизацию внешних проекций при использовании CQRS и EventSourcing?

Я запускаю новое приложение и хочу использовать cqrs и eventourcing. У меня возникла идея воспроизводить события для воссоздания агрегатов и моментальных снимков для ускорения, если это необходимо, используя модели памяти, кеширование и т. Д.

Мой вопрос касается больших моделей чтения, которые я не хочу хранить в памяти. Предположим, у меня есть приложение, в котором я продаю продукты, и я хочу прослушать поток событий, таких как «ProductRegistered», «ProductSold», и построить таблицу в реляционной базе данных, которая будет использоваться для отчетности или интеграции с другой системой. Предположим, есть много записей, и для этой таблицы может потребоваться от нескольких секунд до минут для усечения / восстановления, и приложение экспортирует десятки этих прогнозов для различных целей.

Как обеспечить согласованность прогнозов в этом сценарии?

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

  1. Должен ли я всегда запускать свое приложение с TRUNCATE TABLE + rebuild для каждой внешней проекции? Со временем это кажется мне непрактичным, но, возможно, меня беспокоит проблема, которой у меня еще не было.

  2. Поскольку таблица сама по себе подобна снимку, я мог бы вести «контрольную таблицу», чтобы определять, какое событие было последним, которое я обработал для этой проекции, поэтому я могу воспроизвести только то, что необходимо. Но меня беспокоят несоответствия в случае сбоя приложения или базы данных. Кажется, что проверка согласованности таблицы и перестройка будут такими же, что снова указывает на решение 1.

Как бы вы справились с этим таким образом, чтобы его можно было поддерживать с течением времени? Есть ли лучшие решения?

Большое тебе спасибо.


person Natan    schedule 07.08.2015    source источник
comment
Модель чтения должна (в большинстве случаев) храниться в базе данных, а не оставаться в памяти. Если вы боитесь рассинхронизации из-за сбоя приложения, вам следует подумать о MSMQ для шины событий.   -  person Sir Rufo    schedule 07.08.2015
comment
На самом деле это ничего не решает, даже с очередью сообщений бывают случаи, когда проекция может рассинхронизироваться. Проблема не в транспорте.   -  person Natan    schedule 08.08.2015
comment
Хорошо, если модель чтения не синхронизирована, я воспроизводю все события в модели чтения. И это займет свое время в зависимости от оборудования, базы данных и т. Д. Вы можете минимизировать это рассинхронизацию с помощью транзакционного MQ, потому что это произойдет только тогда, когда сбой произойдет после записи хранилища событий и перед отправкой событий в MQ. И это очень маленький промежуток времени.   -  person Sir Rufo    schedule 08.08.2015
comment
И, как видите, речь идет о том, как вы управляете транспортировкой событий из хранилища событий в проекцию; o)   -  person Sir Rufo    schedule 08.08.2015
comment
Хорошо, теперь я понимаю, о чем вы говорите. Если очередь транзакционная, я бы подтвердил получение сообщения только после записи в таблицу проекции. Это решает проблему гарантии доставки, и вам не нужно усекать таблицу при перезапуске приложения для обеспечения согласованности. Для любой другой проблемы вы бы перестроили, не заботясь о том, сколько времени это займет, поскольку это случается очень редко. Это оно? Вам, вероятно, следует написать что-то в качестве ответа вместо комментария.   -  person Natan    schedule 08.08.2015
comment
Вы пытаетесь найти способ сделать свои прогнозы идемпотентными в инфраструктуре? Я думаю, что трудно найти хороший способ сделать это, поскольку прогнозы могут обрабатывать сообщения по-разному. По моему опыту, легче справиться с этим в каждой проекции, когда это необходимо.   -  person user707727    schedule 08.08.2015


Ответы (1)


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

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

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

person Phil Sandler    schedule 12.08.2015
comment
Спасибо! Это похоже на то, что я получил, изучив исходники NEventStore. Кажется, этого достаточно, чтобы гарантировать заказ проекций на одном сервере. Мне было интересно, как это сделать при настройке с несколькими серверами, но, возможно, для хранилища событий более чем достаточно мастер + резервный сервер. - person Natan; 12.08.2015
comment
Предполагая, что у нас есть одна инкрементная версия по потоку событий, что, если наша проекция будет прослушивать события, поступающие из более чем одного потока? Следует ли нам сохранять текущую версию для каждого потока событий, который мы слушаем? Если да, то что, если наша проекция не отслеживает каждое событие этих потоков, тогда мы не сможем правильно отслеживать версию? - person Maxime Gélinas; 14.05.2019
comment
@ MaximeGélinas Наличие глобальной контрольной точки вместо (или в дополнение к) версии для каждого потока решило бы эти проблемы. Других возможных решений было бы слишком много для комментария. - person Phil Sandler; 15.05.2019
comment
@PhilSandler Я создал новый вопрос по этому поводу здесь: stackoverflow.com/q/56150852/5960632. Буду очень признателен, если вы ответите на него! - person Maxime Gélinas; 15.05.2019