В проекте GitHub CQRS.NET есть несколько конкретных примеров того, как вы можете создавать EventStores с помощью нескольких различных технологий. На момент написания существует реализация на SQL с использованием Linq2SQL и соответствующей схемы SQL , одна для MongoDB , одна для DocumentDB (CosmosDB, если вы находитесь в Azure) и одна с использованием EventStore (как упоминалось выше). В Azure есть больше, например хранилище таблиц и хранилище BLOB-объектов, которое очень похоже на хранилище плоских файлов.
Думаю, главное здесь то, что все они соответствуют одному и тому же принципалу / контракту. Все они хранят информацию в одном месте / контейнере / таблице, они используют метаданные для идентификации одного события от другого и «просто» сохраняют все событие в том виде, в каком оно было - в некоторых случаях сериализовано, в поддерживающих технологиях, как оно было. Таким образом, в зависимости от того, выберете ли вы базу данных документов, реляционную базу данных или даже плоский файл, существует несколько разных способов достижения одного и того же намерения хранилища событий (это полезно, если вы передумаете в любой момент и обнаружите, что вам нужно выполнить миграцию или поддержку более одной технологии хранения).
Как разработчик проекта я могу поделиться некоторыми мыслями о том, что мы сделали.
Во-первых, мы обнаружили (даже с уникальными UUID / GUID вместо целых чисел) по многим причинам последовательные идентификаторы возникают по стратегическим причинам, поэтому просто наличие идентификатора не было достаточно уникальным для ключа, поэтому мы объединили наш основной столбец ключа идентификатора с данными / тип объекта для создания того, что должно быть действительно (в смысле вашего приложения) уникальным ключом. Я знаю, что некоторые люди говорят, что вам не нужно его хранить, но это будет зависеть от того, являетесь ли вы новым участком или должны сосуществовать с существующими системами.
Мы остановились на одном контейнере / таблице / коллекции из соображений удобства обслуживания, но мы поигрались с отдельной таблицей для каждой сущности / объекта. На практике мы обнаружили, что это означает, что либо приложению требуются разрешения «СОЗДАТЬ» (что, вообще говоря, не очень хорошая идея ... как правило, всегда есть исключения / исключения), либо каждый раз, когда новая сущность / объект возникает или развертывается, новая контейнеры для хранения / таблицы / коллекции необходимо сделать. Мы обнаружили, что это было мучительно медленно для локальной разработки и проблематично для производственного развертывания. Возможно, нет, но это был наш реальный опыт.
Также следует помнить, что запрос на выполнение действия X может привести к возникновению множества различных событий, поэтому мы знаем все события, сгенерированные командой / событием / тем, что когда-либо было полезно. Они также могут относиться к разным типам объектов, например, нажатие кнопки «Купить» в корзине для покупок может инициировать запуск событий учетной записи и складирования. Приложение-потребитель может захотеть узнать все это, поэтому мы добавили CorrelationId. Это означало, что потребитель мог запрашивать все события, возникшие в результате его запроса. Вы увидите это на схеме .
В частности, с помощью SQL мы обнаружили, что производительность действительно становится узким местом, если индексы и разделы не используются должным образом. Помните, что события необходимо будет транслировать в обратном порядке, если вы используете снимки. Мы попробовали несколько разных индексов и обнаружили, что на практике некоторые дополнительные индексы необходимы для отладки производственных приложений реального мира. Вы снова увидите это на схеме .
Другие производственные метаданные были полезны во время производственных исследований, отметки времени давали нам представление о порядке, в котором события сохранялись и возникали. Это дало нам некоторую помощь в системе, особенно сильно управляемой событиями, которая генерировала огромное количество событий, давая нам информацию о производительности таких вещей, как сети, и о распределении систем по сети.