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

Общение между двумя ограниченными контекстами в DDD

У меня есть несколько разных ограниченных контекстов в домене. Валидация операции CRUD строится в каждом ограниченном контексте.

Например, я могу создать объект, называемый GAME, только если человек, создающий его, является Лидером группы.

В этом примере у меня есть два ограниченных контекста (BC). Один из них - Game BC, а другой - Пользователь BC. Чтобы решить проблему, в Game BC, я должен сделать вызов службы домена, например IsGroupLeader(), в Пользователь BC, прежде чем продолжить создавая игру.

Я не думаю, что этот тип связи рекомендуется DDD. Я могу иметь объект User также в Game BC, но я не хочу, потому что один и тот же пользовательский объект используется по-разному в другом контексте в другом BC.

Мои вопросы:

  • Должен ли я использовать события домена, где Game BC должен отправить событие в Пользователь BC с запросом статуса пользователя? При таком подходе я не делаю синхронный вызов, например IsGroupLeader, но событие с именем is_group_leader. Затем Game BC должен дождаться, пока пользователь BC BC обработает событие и вернет статус. Игра BC создаст объект игры только после того, как событие BC BC обработает событие.

  • Является ли CQRS решением моей проблемы?

Любая идея оценена.

4b9b3361

Ответ 1

При интеграции BCs у вас есть несколько вариантов. Причина, по которой призыв к внешнему ВС обескураживается, заключается в том, что для одновременного использования обоих BC требуется. Однако это часто вполне приемлемо и проще, чем альтернатива. Альтернативой является то, что Game BC подписывается на события от пользователя BC и сохраняет локальные копии необходимых ему данных, что в данном случае является информацией о том, является ли пользователь лидером группы. Таким образом, когда Game BC необходимо определить, является ли пользователь лидером группы, ему не нужно вызывать пользователя BC, он просто считывает локально сохраненные данные. Задачей этой event-driven альтернативы является синхронизация событий. У вас есть удостоверение, что игра BC получает все соответствующие события от пользователя BC. Еще одна проблема связана с конечной согласованностью, поскольку BC могут немного не синхронизироваться в любой момент времени.

CQRS несколько ортогонален этой проблеме.

Ответ 2

Вот как я мог бы рассуждать об этом.

Я бы сказал, что Game BC не знает о "Пользователях", однако он может знать о "Игроках".

Если игра BC зависит от активного/текущего игрока, то она должна быть передана в BC при создании экземпляра Game BC.

например.

 Player currentPlayer = GetPlayerSomehow...();
 GameBC gameBC = new GameBC(currentPlayer);
 gameBC.DoStuff();

Теперь ваши два BC все еще разделены, вы можете их протестировать отдельно и т.д.

И чтобы все это работало, вы просто делаете что-то вроде:

 User currentUser = GetCurrentUser();
 Player currentPlayer = new Player();
 currentPlayer.IsGroupLeader = currentUser.IsGroupLeader;
 GameBC gameBC = new GameBC(currentPlayer);
 gameBC.DoStuff();

Это служит антикоррупционным слоем между UserBC и GameBC, вы можете перемещать и проверять состояние, которое вы хотите от UserBC, в состоянии, которое вам нужно для GameBC.

И если ваш GameBC должен получить доступ ко многим пользователям, вы все равно можете передать какую-то услугу сопоставления в игру BC, которая делает это преобразование внутренне.

Ответ 3

Я думаю, ты почти там. Близко к хорошему решению. Я не уверен, что вы должны разделить эти два на два БК. Ваш пользователь Aggregateroot (?) И игра могут принадлежать одному BC и зависят друг от друга. Пользователь "имеет" членство "в одной или нескольких" играх "(просто угадывая ваши сущностные отношения). Но сейчас я просто мозговой штурм. Попробуйте следовать:) Следуют следующие подходы:

Первая GameBC имеет метод Create(), который фактически принимает UserMembership как param. Создать (UserMembership). Затем вы через сущность UserMembership знаете, что такое членство и пользователь. Если это принято, игра создается. Если не выбрано исключение, или Game получает сообщение с нарушенным правилом, зависит от того, какой подход вы хотите вернуть клиенту. Координация может выполняться на прикладном уровне без утечки знаний домена.

Второй Вы делаете это как один из других ответов. Вы создаете CreateGameEvent в методе Game.Create(UserId). Это событие улавливается EventHandler (зарегистрированным IoC при запуске приложения), который находится на уровне приложения и просматривает UserMembership через репозиторий. Небольшая утечка знаний о домене - это бизнес-правило, которое знает, кому разрешено делать то, что проверено на прикладном уровне. Это можно решить, разрешив CreateGameEventHandler использовать UserId и RuleRef (может быть строка "CAN_CREATE_GAME" или перечисление) и позволить объекту UserPermission проверить разрешение. Если не. Исключение выбрасывается и попадает в прикладной уровень. Недостатком может быть то, что вы хотите, чтобы ссылки на ссылки справки были жестко закодированы в методе Create.

Третий ... продолжается, когда заканчивается второй подход. Вы знаете, что GameBC не может быть подходящим местом для поиска пользовательских прав, если следовать принципу SRP. Но действие так или иначе срабатывает вокруг этого метода. Альтернативой будет Create (пользователь GroupLeader). ИЛИ вы можете иметь Game.Create(Пользователь пользователя), затем выполнить проверку того, что User является типом GroupLeader. Create (GroupLeader) сообщает, что вам нужно вызвать этот метод.

Последняя Возможно, альтернатива, которую мне больше нравится сейчас при написании этого. Когда вы хотите создать объект, я обычно позволяю этому методу Create (Save) находиться в репозитории. Интерфейс IGameRepository расположен рядом с Game Entity в проекте сборки домена. Но вы также можете создать GameFactory, который отвечает за запуск жизненного цикла Game Entity. Здесь также хорошее место для создания метода Create... GameFactory.Create(GroupLeader) {return new Game.OwnerUserId = GroupLeader.Id; } Тогда вы просто сохраните его IGameRepository.Save(Game)

Затем у вас есть интуитивный и самоописывающий способ сказать другим разработчикам, что "у вас должен быть экземпляр GroupLeader для создания игры".

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

Удачи.

Ответ 4

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

Это должно быть очевидно, учитывая, что разные роли сталкиваются с различными проблемами и поэтому говорят немного (или полностью) разные языки.

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

Что касается вашего второго вопроса, CQRS может быть способом кодирования типов взаимодействий между BC, которые вы описываете, но мне это не нравится в этом конкретном контексте.

Ответ 5

Я думаю, что мне, возможно, придется придерживаться другого подхода, когда я сделаю часть User entity Game BC (тот же объект является частью Пользователя BC). Я буду использовать репозиторий для чтения флага IsGroupLeader из db в Game BC. Таким образом, связь с Пользователем BC удаляется, и связь с Пользователем BC не требуется).

Как вы думаете?