Нахождение совокупного идентификатора на основе зависимости - PullRequest
0 голосов
/ 18 апреля 2019

У меня довольно запутанный вопрос, так как я не уверен, что это скорее глобальная проблема дизайна.

У меня есть служба, которая определяет некоторые агрегаты, и эти агрегаты публикуют события.Давайте выберем один агрегат из сервиса и назовем его A. Теперь, определяя другой сервис, который имеет некоторый агрегат, который должен работать с A, давайте вызовем второй агрегат B.

Когда A публикует какое-то событие, яЯ хочу отправить команду B (в идеале через сагу), но мне трудно вычислить соответствующий идентификатор B (A не знает, что B существует, поэтому события, которые он публикует, не имеют никаких подсказок о том, как рассчитать id)

Я могу подумать о нескольких возможных сценариях:

Сначала будет статический расчет идентификатора B на основе идентификатора A, например, в аксоне я мог бы сделать что-то вроде some-id-${suffix}, поэтомукогда я получаю событие от A с some-id, я сразу могу знать, что оно должно быть отправлено на some-id-B

Второй будет использовать сторону чтения?(я не уверен, как его правильно называть) и запросить вещь и попытаться найти B id, основанный на A id, но это кажется немного излишним.

Есть ли что-нибудь, что я мог прочитать, который мог бы провести меня черезвозможные сценарии и подскажите, как с ними справиться?Спасибо!

1 Ответ

2 голосов
/ 18 апреля 2019

Из того, что я понимаю, у вас есть отношение от агрегата B к агрегату A. Такие отношения нормальны и они возникают постоянно.

Примечание. Поскольку этот вопрос носит весьма общий характер и не имеет контекста, я могу что-то упустить.Если есть более особый случай, чем описанный, сообщите мне об этом.

Это отличное прочтение для совокупного дизайна

Примечание:Проверьте это видео от Мартина Фаулера, прежде чем читать оставшуюся часть этого ответа, я настоятельно рекомендую его, поскольку он очень подробно объясняет понятия, связанные с событиями и командами.

Примечание: Поскольку термин «сущность» также очень важен, с этого момента я больше не буду использовать агрегат, поэтому предположим, что каждая сущность ( Игрок , Пользователь , Игра )являются корнем их собственной совокупности и являются границей согласованности, поэтому будет использоваться возможная согласованность в этом случае с событиями домена.Я также буду игнорировать CQRS в данный момент, чтобы избежать необходимости говорить о стороне чтения и стороне записи.Мы обсудим и внедрим Модель

Давайте рассмотрим пример с игрой.У вас есть Player , который должен представлять User в Game .Сущность Player должна каким-либо образом ссылаться на Game и User .Это может быть по прямой ссылке или по идентификатору.В случае распределенной системы это будет по ID.В нашем примере давайте используем идентификаторы UUID (например, 8d670541-aa51-470b-9e72-1d9227669d0c ) для идентификаторов, поэтому мы можем генерировать их случайным образом без определения схемы, автоматически генерировать порядковый номер (как в базах данных SQL)или специальный алгоритм.Предположим, что Пользователь имеет UserStatistics .Таким образом, когда Player набирает очки (например, убивая других игроков в стрелялке), сущность UserStatistics должна быть создана, если она не существует и не обновлена. UserStatistics должен также ссылаться на User по идентификатору, поэтому у нас есть зависимость от UserStatistics до User .

UserStatistics будет выглядеть следующим образом:

UserStatistics {
  UUID UserID,
  uint KillsCount,
  uint GamesPlayedCount
}

Поскольку Player не может существовать без User , User долженбыть создан первым.Поскольку Player является частью Game , это означает, что Game следует создать в первую очередь.Давайте определим некоторую терминологию в нашем вездесущем языке .A Пользователь 'присоединяется' a Игра , становясь Игроком в ней.Предположим, что игра будет создана кем-то другим, а не Пользователи , чтобы избежать необходимости обсуждать ситуацию, когда Пользователь создает игру и должен присоединиться к ней в то же время и т. Д.это происходит в той же транзакции и т. д. ... Игра будет чем-то похожим на MMO, где она создается кем-то, и обычные пользователи могут присоединиться.

Когда Пользователь присоединяется к a Game , тогда сущность Player будет создана с userID и gameID .Создание Player без userID и gameID недопустимо.

Давайте обсудим проблему с Команды и События . Команды могут быть Инициированы на События . Давайте использовать Наблюдательский паттерн . Одна сущность должна будет наблюдать за другой сущностью для событий. В нашем примере это означает, что зависимость от UserStatistics (наблюдатель) до Пользователь и Игрок (субъект / производитель сообщений). Тот факт, что конкретная Команда на UserStatistics будет выполнена как реакция на Событие , поднятое с Игрок и Пользователь никоим образом не должен влиять на игрока или игрока . Использование Event для преднамеренного запуска специальной Command в пассивном агрессивном стиле - не очень хорошая стратегия. Команды могут быть вызваны Событием , но не только одна конкретная Команда может быть Триггером . Много различных команд могут быть запущены, и только зависимые сущности , Службы или Системы должны заботиться о том, что происходит. Игрок и Пользователь просто предоставляют События .

Когда Пользователь присоединяется к Game и Player создается, он будет ссылаться на обе сущности по ID, поэтому он будет выглядеть примерно так:

Player {
  UUID GameID,
  UUID UserID
}

Также UserJoinedGameEvent событие будет инициировано из Пользователь сущности (оно может быть поднято из Игра , но мы выберем Пользователь ). Это выглядит так:

UserJoinedGameEvent {
  UUID GameID,
  UUID UserID,
  UUID PlayerID
}

UserStatisticsService может подписаться на события и обновлять статистику.

Когда Пользователь присоединяется к Игре , начнется процесс сбора статистики, и мы обновим (или создадим, если она не существует) его UserStatistics Сколько игр он сыграл. В то же время, когда Player совершает убийство, нам придется снова обновлять статистику.

StartGatheringUserStatisticsCommand будет запускаться из UserJoinedGameEvent события.

Давайте добавим событие PlayerMadeKillEvent , которое выглядит следующим образом:

PlayerMadeKillEvent {
 UUID UserID,
 UUID PlayerID,
 UUID GameID
}

UserStatisticsService подпишется на PlayerMadeKillEvents и обновит UserStatistics , используя PlayerMadeKillEvent.UserID , чтобы найти статистику для конкретного Пользователь .

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

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

...