CQRS Event Sourcing проверяет имя пользователя уникально или нет в EventStore при отправке команды

EventSourcing отлично работает, когда у нас есть уникальный уникальный EntityID, но когда я пытаюсь получить информацию от eventStore, отличного от определенного EntityId, у меня тяжелое время.

Я использую CQRS с EventSourcing. В качестве части событий-источников мы сохраняем события в таблице SQL в виде столбцов (EntityID (uniqueKey), EventType, EventObject (например, UserAdded)).

Таким образом, сохраняя EventObject, мы просто сериализуем объект DotNet и сохраняем его в SQL. Таким образом, все данные, связанные с событием UserAdded, будут в формате xml. Меня беспокоит, что я хочу убедиться, что имя пользователя, которое присутствует в db, должно быть уникальным.

Итак, при выполнении команды AddUser мне нужно запросить EventStore (sql db), будет ли конкретное имя пользователя уже присутствовать в eventStore. Поэтому для этого мне нужно сериализовать все события UserAdded/UserEdited в хранилище событий и проверьте, запрашивается ли имя пользователя в eventStore.

Но поскольку часть команд CQRS не разрешается запрашивать, может быть из-за условия гонки.

Итак, я попытался перед отправкой команды AddUser просто запросить eventStore и получить все пользовательские имена, выполнив сериализацию всех событий (UserAdded) и выберев имена пользователей, и если запрошенное имя пользователя уникально, тогда запустите команду else, чтобы исключить, что имя пользователя уже существует.

Как и в случае с вышеприведенным подходом, нам нужно запросить весь db, и у нас могут быть сотни тысяч событий/дней. Таким образом, выполнение запроса/десериализации займет много времени, что приведет к проблемам с производительностью.

Я ищу лучший подход/предложение для поддержания имени пользователя. Уникально либо путем получения всех userNames из eventStore, либо любого другого подхода.

Ответ 1

Таким образом, ваш клиент (то, что выдает команды) должен быть полностью уверен в том, что отправленная им команда будет выполнена, и он должен сделать это, убедившись, что перед отправкой RegisterUserCommand, что никто другой не зарегистрирован этот адрес электронной почты. Другими словами, ваш клиент должен выполнить проверку, а не ваш домен или даже приложения, которые окружают домен.

От http://cqrs.nu/Faq

Это часто встречающийся вопрос, поскольку мы явно не выполняя операции кросс-агрегации со стороны записи. Мы делаем, однако, есть ряд вариантов:

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

Создайте реактивную сагу, чтобы отметить и инактивировать учетные записи, которые были тем не менее, создан с дублирующимся именем пользователя. (Чрезмерно совпадение или злонамеренное или из-за неисправного клиента.)

Если возможная последовательность недостаточно для вас, подумайте о добавлении таблица на стороне записи, небольшая локальная сторона чтения как бы, из уже выделенные имена. Сделать совокупную транзакцию вставляя в эту таблицу.

Ответ 2

Как часто, нет правильного ответа, только ответы, которые соответствуют вашему домену.

Вы находитесь в среде, которая действительно требует немедленной согласованности? Каковы были бы шансы идентичного имени пользователя, создаваемого между уникальностью момента, проверяется путем запроса (скажем, на стороне клиента) и когда команда обрабатывается? Могут ли ваши эксперты домена терпеть, например, один из 1 миллиона конфликтов имен пользователей (что может быть компенсировано впоследствии)? У вас будет миллион пользователей в первую очередь?

Даже если требуется немедленная согласованность, "имена пользователей должны быть уникальными"... в какой области? A Company? An OnlineStore? A GameServerInstance? Можете ли вы найти наиболее ограниченную область, в которой должно быть ограничено единственность, и сделать эту область Агрегированным корнем, из которой будет прорастать нового пользователя? Почему "повторить все события с добавлением пользователей/пользователей" было бы плохо, если бы Агрегатный корень делал эти события малыми и простыми?

Ответ 3

С GetEventStore (от Грега Янга) вы можете использовать любую строку как ваш aggregateId/StreamId. Используйте имя пользователя как идентификатор агрегата, а не гида, или комбинацию типа "mycompany.users.john" в качестве ключа и.. voila! У вас есть уникальная уникальность имени пользователя!

Ответ 4

Запросы различных агрегатов с хранилищем в операции записи как часть вашей бизнес-логики не запрещены. Вы можете сделать это, чтобы принять команду или отклонить ее из-за дублирования пользователя с помощью некоторой доменной службы (операция перекрестного агрегирования). Грег Янг упоминает об этом здесь: https://www.youtube.com/watch?v=LDW0QWie21s&t=24m55s

В обычных сценариях вам просто нужно запросить все события UserCreated + UserEdited. Если вы ожидаете иметь тысячи таких событий в день, возможно, ваши события раздуты, и вы должны разрабатывать более атомарно. Например, вместо того, чтобы UserEdited событие UserEdited каждый раз, когда что-то происходит с пользователем, рассмотрите возможность использования UserPersonalDetailsEdited и UserAccessInfoEdited или аналогичных, где поля, которые должны быть уникальными, обрабатываются иначе, чем остальные пользовательские поля. Таким образом, запрос всех UserCreated + UserAccessInfoEdited перед принятием или UserAccessInfoEdited команды будет более легкой операцией.

Лично я бы пошел со следующим подходом:

  1. Больше атомарности в событиях, так что все, что касается полей, которые должны быть глобально уникальными, описано более подробно (например, UserCreated, UserAccessInfoEdited)
  2. Подготовьте проекции на стороне записи, чтобы запросить их во время операции записи. Так, например, я бы подписался на все UserCreated и UserAccessInfoEdited, чтобы сохранить запрашиваемую "таблицу" со всеми уникальными полями (например, электронная почта).
  3. Когда команда CreateUser поступает в домен, доменная служба запрашивает эту таблицу электронной почты и принимает или отклоняет команду.

Это решение в некоторой степени зависит от возможной согласованности, и существует возможность, когда запрос сообщает нам, что поле не использовалось, и позволяет команде успешно UserCreated событие UserCreated когда фактически проекция еще не была обновлена из предыдущей транзакции, вызывая поэтому ситуация, когда в системе есть 2 поля, которые не являются глобально уникальными.

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