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
команды будет более легкой операцией.
Лично я бы пошел со следующим подходом:
- Больше атомарности в событиях, так что все, что касается полей, которые должны быть глобально уникальными, описано более подробно (например,
UserCreated
, UserAccessInfoEdited
) - Подготовьте проекции на стороне записи, чтобы запросить их во время операции записи. Так, например, я бы подписался на все
UserCreated
и UserAccessInfoEdited
, чтобы сохранить запрашиваемую "таблицу" со всеми уникальными полями (например, электронная почта). - Когда команда
CreateUser
поступает в домен, доменная служба запрашивает эту таблицу электронной почты и принимает или отклоняет команду.
Это решение в некоторой степени зависит от возможной согласованности, и существует возможность, когда запрос сообщает нам, что поле не использовалось, и позволяет команде успешно UserCreated
событие UserCreated
когда фактически проекция еще не была обновлена из предыдущей транзакции, вызывая поэтому ситуация, когда в системе есть 2 поля, которые не являются глобально уникальными.
Если вы хотите полностью избежать этих неопределенных ситуаций, потому что ваш бизнес не может справиться с возможной последовательностью, я рекомендую разобраться с этим в вашей области, явно моделируя их как часть вашего вездесущего языка. Например, вы могли бы по-разному моделировать ваши агрегаты, поскольку очевидно, что ваш пользователь агрегата на самом деле не является вашей транзакционной границей (то есть: это зависит от других).