Межагрегатная связь

В настоящее время у меня есть один поток событий на корневой агрегат и два корневых агрегата, Room и RoomType.

Поведение Room зависит от того, что это за RoomType. Чтобы разделить оба агрегата, RoomType просто представлен как roomTypeId в агрегате Room. Изменение RoomType представлено событием RoomTypeChanged.

RoomTypes можно управлять отдельно, и он должен находиться в другом агрегате.

Теперь рассмотрим следующий вариант использования:

Когда пользователь аннулирует RoomType, все Rooms, у которых есть этот Roomtype, должны переключиться на резервный вариант RoomType.

Я думал о нескольких подходах, но все они кажутся проблематичными:

  1. Пусть прослушиватель событий прослушивает RoomTypeInvalidated-событие и отправляет SwitchToFallbackRoomType всем Room-агрегатам, которые имеют это Roomtype. Как бы я это сделал? Невозможно узнать, какие агрегаты имеют этот Roomtype, если я не получу доступ к своей модели чтения, что кажется неправильным. Даже если бы я загружал все агрегаты, невозможно загрузить только агрегаты этого типа, поскольку я не могу загрузить подмножество всех потоков (используя geteventstore).

  2. При повторном применении событий RoomTypeChanged к агрегату Room вместо того, чтобы просто применить его, проверьте, существует ли еще этот RoomType, но опять же, как узнать, какие RoomTypes существуют (я был бы в той же ситуации, что и 1 , но перевернутый)? Кроме того, кажется неправильным вводить логику повторного применения событий, я думаю, они должны просто представлять изменения состояния.

Как бы вы решили это?


person Kenneth    schedule 13.02.2015    source источник


Ответы (3)


Почему бы не использовать диспетчера процессов для каждого типа комнаты и не иметь список комнат в качестве свойства диспетчера процессов?
Запрос модели чтения из службы или из обработчика команд не гарантирует согласованной структуры. Что делать, если комната была назначена недействительному типу комнаты, но модель чтения еще не обновлена? (это своего рода сценарий, с которым я сталкиваюсь)

person hsen    schedule 18.06.2015
comment
Проблема с диспетчером процессов для каждого типа комнаты заключается в том, как обновляется его состояние. Если вы обновите его в той же команде, которая создает комнату, вам теперь нужны транзакции, чтобы гарантировать, что состояние БД остается согласованным (у вас есть 2 операции записи: комната и менеджер). Если, с другой стороны, вы обновляете диспетчер процессов из обработчика событий, то у вас есть другая версия той же упомянутой проблемы OP. - person Carlos G.; 25.09.2015

Решение этой проблемы путем обеспечения полной согласованности данных каждый раз работает против дизайна. Event Sourcing предполагает, что система непротиворечива в конечном счете, поэтому наличие несоответствий в модели чтения является нормальным и ожидаемым.

Лучший способ решить эту проблему — запросить вашу модель чтения для всех комнат с новым недействительным TypeId (ваше решение 1). После обновления всех этих комнат существует вероятность того, что несколько отставших не были обновлены, поэтому вы повторно запускаете обработчик событий через несколько секунд (скажем, через минуту).

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

person Carlos G.    schedule 24.09.2015
comment
Возможная согласованность — это хорошо, но непоследовательность — нет. Откуда ты знаешь, сколько ждать? 1 секунда, 1 минута или сутки? Если вы не ждете достаточно долго, ваши данные несовместимы (не только ваша модель чтения). - person Kenneth; 25.09.2015
comment
Потребовалось много времени, чтобы ответить на этот вопрос, но вот. Окончательная согласованность по определению подразумевает систему, допускающую несогласованное состояние. Если вам нужна полная 100% согласованность в любое время, используйте ACID. Чтобы решить проблему продолжительности, вы выполняете проверку несколько раз, пока данные не стабилизируются. Например, запускайте его каждую минуту, пока не останется комнат для обновления. И затем, вы можете повторно запустить его снова через час. Кроме того, чтобы избежать загрязнения, вы можете выполнить дополнительную проверку при доступе к этому свойству в другой команде, чтобы убедиться, что вы не используете устаревшие данные. - person Carlos G.; 01.10.2015
comment
Проблема в том, что вы не знаете, когда больше не будет комнат для обновления, поэтому вам придется запускать его вечно. Это преувеличено, но вопрос остается в силе, как долго вы продолжаете повторять это? Вы пытаетесь решить проблему согласованности, читая из возможного согласованного источника. - person Kenneth; 01.10.2015
comment
Я понимаю, что это может звучать немного безумно, но именно так работают в конце концов непротиворечивые системы. Если вы прекратите добавлять данные в систему и продолжите выполнение задачи, в какой-то момент старые данные не останутся, и система станет согласованной. Конечно, если вы продолжите выполнять операции, система никогда не стабилизируется полностью, поскольку, пока старые операции становятся стабильными, новые еще неизвестны. - person Carlos G.; 06.10.2015

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

При переосмыслении мы пришли к агрегатам, таким как RoomAssignment и RoomAllocationSchedule, и обнаружили, что RoomType в основном используется для передачи функций комнаты внешним каналам / OTA.

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

Когда нам действительно нужна согласованность между агрегатами, мы моделируем процесс-менеджер/сагу, который сам по себе согласован с транзакциями, и отправляем сообщения агрегатам. Таким образом, у нас есть агрегат «RoomAssignmentDirector», который гарантирует, что комната будет выделена для проживания в комнате, прежде чем она будет назначена для проживания в комнате и т. д.

Надеюсь, поможет.

person Julian    schedule 07.10.2015