Подтвердить что ты не робот

Валидация уникальности при использовании CQRS и источника событий

Я пытаюсь реализовать свою собственную инфраструктуру CQRS с помощью Event Sourcing, чтобы узнать ее лучше. В качестве образца проекта я реализую механизм блога, я знаю, что это может быть не совсем идеально, но я просто хочу работать над чем-то реальным.

Проблема, к которой я пришел сейчас, - это проверка. Каждый пост имеет shortUrl, а shortUrl должен быть уникальным, но где я должен помещать эту проверку в домен? Я знаю, что у меня будет эта проверка, прежде чем я даже отправлю команду, читая из моего магазина чтения, чтобы проверить, действительно ли это при создании команды создания сообщения или команды обновления после.

Я могу думать о двух "решениях".

  • У вас есть агрегат Blog, который отслеживает все настройки, связанные с блогами, а также ссылки на все сообщения. Но проблема с этим в моих глазах заключается в том, что я должен обрабатывать связь между агрегатами в этом сценарии, а также каждый раз, когда мне нужно проверить уникальность shortUrl Мне нужно прочитать все события из хранилища событий, чтобы создать все сообщения, и это кажется сложным.
  • Второй вариант, который у меня есть, - это когда событие запускается, и мой обработчик событий, который создает модель чтения, запускает дубликат короткого URL-сообщения, когда он замечает, что у него будут два коротких URL-адреса, указывающие на разные сообщения. Действительно ли, чтобы считываемая модель срабатывала при обнаружении ошибок?

Есть ли еще альтернативы. Заметьте, что я знаю, что мой домен может быть не лучшим образом подходит для cqrs и DDD, но я делаю это, чтобы учиться в небольшом домене.

4b9b3361

Ответ 1

Я бы пошел на службу домена, которая просто ответственна за создание уникальных ShortURL. Вы можете использовать транзакционную БД для реализации этого поведения. Обычно эта служба будет использоваться частью обработки команд агрегата BlogPost. Если есть дубликат ShortURL, вы можете запустить DuplicateUrlErrorEvent. Вы можете предварительно уловить это в пользовательском интерфейсе (но не на 100%), создав тонкую модель запроса с использованием одного и того же источника данных, поэтому вы можете запросить, является ли укороченный URL уникальным, прежде чем отправлять сообщение (как описано в ответе @RyanR).

Ответ 2

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

Ответ 3

Я прочитал различные ответы на этот вопрос и связанный с ним вопрос.

Решение сводится к правильности. Если вы можете прощать и принимать несовершенное поведение для некоторой степени операции, ваша проблема гораздо проще решить, особенно при слабых гарантиях последовательности.

Однако, если вы хотите согласованности, вы должны использовать службу сохранения, которая имеет сильные гарантии последовательности.

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

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

Однако, поскольку у нас есть две транзакции, не обязательно в одной и той же базе данных, мы можем выйти из строя после первого коммита. Это нормально, потому что операция в целом также потерпит неудачу. Магазин чтения будет отображаться несогласованное состояние в течение некоторого времени, но как только мы восстановим агрегат, память чтения вернется в согласованном состоянии.

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

Был пример, в котором банк разрешил бы пользователю перевыполнять свою учетную запись, потому что у нее есть надбавки для компенсации. Это вызывает вопросы, потому что казалось бы небрежным решить такую ​​проблему, даже ленив. Некоторые называют это умным. Я не знаю, что думать. У банка, вероятно, достаточно денег, чтобы покрыть его, поэтому они могли бы игнорировать его, но это не так, как сейчас работает мир. Во всяком случае, я отвлекаюсь.

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