Как мы можем сделать нашу обработку сообщений идемпотентной?
Вам следует:
- игнорировать сообщения, которые вы уже "видели";это означает, что потребитель должен иметь способ обнаружить это;например, он может хранить список идентификаторов сообщений, которые он обработал (это означает, что каждое сообщение должно иметь уникальный идентификатор).
- не выдает исключение, если сообщение не изменяет состояние;например, если вы получаете второе событие
DeleteUser
(таким образом, пользователь уже удален, второе удаление не должно иметь побочных эффектов), тогда вы игнорируете его.Не каждое событие может быть идемпотентным, например, UpdateUserName
не должно быть идемпотентным.
Если мы сохраняем каждое событие в хранилище событий, следует ли учитывать, когдаразработка полезной нагрузки каждого события и агрегации событий для получения агрегатного состояния?
Вы должны разрабатывать события на основе вашего домена;полезная нагрузка должна не содержать больше информации, чем это необходимо для домена.Если дополнительная информация сделает ваши readmodels намного проще для реализации, вы можете добавить ее в полезную нагрузку, но будьте осторожны, чтобы пометить ее как избыточную.
Пример: допустим, у нас есть сообщения «Пользователь создан» и «Пользователь удален» (или любая другая пара событий, которые НУЖНО обрабатывать по порядку).Если мы обработаем «Пользователь удален» до «Пользователь создан», пользователь не будет удален.Даже если они идут в порядке очереди в очереди.Может ли действительно идемпотентная обработка / идемпотентные события давать удаленному пользователю?
В этом конкретном случае у вас может быть дополнительная коллекция удаленных пользователей;Вы можете сохранить только их удостоверение личности.Когда приходит событие CreateUser
, вы можете проверить, был ли пользователь уже удален, заглянув в коллекцию DeletedUsers
, и проигнорировать его, если он там есть.Вы можете игнорировать любое другое событие, которое происходит для этого пользователя.
Это решение очень зависит от домена.
Другой пример.Давайте предположим, что у нас есть объект с атрибутом Score.Пользователь может изменить счет.Вторая служба использует события службы «объект оценки», и если счет достигает 100, объект (или ссылка на объект) вставляется второй службой в объект «Лучшая категория».Если оценка достигает -20, вторая служба добавляет объект оценки в «Худшую категорию».Наличие множественного набора второй службы может дать непредсказуемый результат, если события «100 баллов» и «20 баллов» находятся в крошечном интервале времени.Любые идеи о том, как проектировать события «Score x» или как обрабатывать эти события?
Эту ситуацию можно решить, сохранив in-the / attach-to readmodels (две коллекции, * 1039).* и worse
) отметка времени / порядок / поток-версия / независимо от последнего обработанного события и игнорирует каждое событие, которое меньше или равно этой отметке времени.Таким образом, если «оценка 100» выбрасывается после «оценки -20», но прибывает первой, следует игнорировать «оценку -20», потому что она имеет более низкую отметку времени, хотя она идет последней.
Это решение является общим, но оно основано на существовании некоторого порядка.