Я проектирую Event Store на AWS и выбрал DynamoDB, потому что это казалось лучшим вариантом. Мой дизайн выглядит неплохо, но я столкнулся с некоторыми проблемами, которые не могу решить.
** Дизайн
События однозначно идентифицируются парой (StreamId, EventId)
:
StreamId
: то же самое, что и для aggregateId, что означает один Поток событий для один Агрегат.
EventId
: инкрементное число, которое помогает сохранить порядок в том же потоке событий
События сохраняются в DynamoDb. Каждое событие отображается на одну запись в таблице, где обязательными полями являются StreamId, EventId, EventName, Payload (можно легко добавить больше полей).
PartitionKey - это StreamId, sortKey - это EventId.
Оптимистическая блокировка используется при записи события в поток событий. Для этого я использую условные записи DynamoDb. Если событие с таким же (StreamId, EventId) уже существует, мне нужно пересчитать агрегат, перепроверить бизнес-условия и, наконец, записать заново, если бизнес-условия пройдут.
Потоки событий
Каждый поток событий определяется ключом partitionKey. Запрос потока для всех событий равен запросу для partitionKey = $ {streamId} и sortKey между 0 и MAX_INT.
Каждый поток событий идентифицирует один и только один агрегат. Это помогает обрабатывать параллельные записи в одном агрегате с использованием оптимистической блокировки, как описано выше. Это также обеспечивает высокую производительность при повторном вычислении агрегата.
Публикация событий
События публикуются с использованием комбинации DynamoDB Streams + Lambda.
Воспроизведение событий
Вот здесь и начинаются проблемы. При сопоставлении каждого потока событий только с одним агрегатом (что приводит к большому количеству потоков событий), нет простого способа узнать, из каких потоков событий мне нужно запрашивать все события.
Я думал об использовании дополнительной записи где-нибудь в DynamoDB, которая хранит в массиве все StreamIds. Затем я могу запросить его и начать запрашивать события, но если во время воспроизведения будет создан новый поток, я потеряю его.
Я что-то упустил? Или мой дизайн просто неправильный?