web-dev-qa-db-ja.com

DDDでの2つの境界コンテキスト間の通信

ドメインにいくつかの異なるBounded Contextsがあります。 CRUD操作の検証は、各境界コンテキストに組み込まれています。

たとえば、GAMEというエンティティを作成するのは、それを作成した人がグループリーダーである場合のみです。

この例では、2つの境界コンテキスト(BC)があります。 1つはゲームBCで、もう1つはユーザーBCです。この問題を解決するには、ゲームBCIsGroupLeader()のようなドメインサービス呼び出しをユーザーBCに対して実行する必要があります。ゲームを作成します。

このタイプの通信はDDDで推奨されているとは思いません。 ユーザーエンティティゲームBCにも含めることができますが、同じユーザーエンティティが別の方法で使用されているため、使用したくありません。異なるBCの異なるコンテキスト。

私の質問は:

  1. ゲームBCユーザーBCにイベントを送信してユーザーのステータスを要求するドメインイベントを使用する必要がありますか?このアプローチでは、IsGroupLeaderのような同期呼び出しは行いませんが、is_group_leaderというイベントを呼び出します。次に、ゲームBCは、ユーザーBCがイベントを処理してステータスを返すのを待つ必要があります。ゲームBCは、ユーザーBCがイベントを処理した後にのみ、ゲームエンティティを作成します。

  2. CQRSは私の問題の解決策ですか?

どんなアイデアでも感謝します。

27
wonderful world

BCを統合する場合、いくつかのオプションがあります。外部BCを呼び出すことが推奨されないのは、両方のBCが同時に動作可能である必要があるためです。ただし、これは多くの場合非常に許容可能であり、他の方法よりも簡単です。別の方法は、ゲームBCにユーザーBCからのイベントをサブスクライブさせ、必要なデータのローカルコピーを保持させることです。この場合は、ユーザーがグループリーダーであるかどうかに関する情報です。このように、ゲームBCは、ユーザーがグループリーダーであるかどうかを判断する必要がある場合、ユーザーBCを呼び出す必要はなく、ローカルに保存されたデータを読み取るだけです。この event-driven の課題は、イベントを同期させることです。ゲームBCがユーザーBCから適切なイベントをすべて受信することを確認します。別の課題は、BCが特定の時点でわずかに同期していない可能性があるため、 結果整合性 を処理することです。

CQRSは、この問題に対してある程度直交しています。

27
eulerfx

これが私がそれについて推論する方法です。

ゲームBCは「ユーザー」については知らないが、「プレーヤー」については知っているかもしれません。

ゲームBCがアクティブ/現在のプレーヤーに依存している場合は、ゲームBCインスタンスを作成するときに、BCに渡す必要があります。

例えば。

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

これで2つの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に渡すことができます。

16
Roger Johansson

あなたが直面している種類の問題に対処するために、私たちは 限定された役割 を使用しています。これは、長年にわたって出現し、非常にうまく機能することが証明されたモデリングパターンです。境界付きコンテキストは、多くの場合、企業組織では特定の役割にマッピングできる意味単位の後に定義されます。

さまざまな役割がさまざまな問題に直面しているため、わずかに(または完全に)異なる言語を話すことを考えると、それは明らかです。

したがって、アプリケーションとやり取りするロールは常に、アプリケーション要件(インフラストラクチャ、永続性、UI、ローカリゼーションなど...)とビジネスルール(ドメイン)の接点としてモデル化し、それらを異なるモジュール(別名アセンブリまたはパッケージ)。

2番目の質問と同様に、CQRSは、記述しているBC間の種類の相互作用をコード化する方法になる可能性がありますが、この特定のコンテキストでは好きではありません。

2
Giacomo Tesio

もうすぐだと思います。良い解決策に近い。これら2つを2つのBCに分割する必要があるかどうかはわかりません。ユーザーAggregateroot(?)とゲームは1つのBCに属し、互いに依存している可能性があります。ユーザーは「1つまたは複数の」ゲームに「メンバーシップ」を持っています(エンティティの関係を推測するだけです)。しかし、私は今ブレーンストーミングをしています。 :)異なるアプローチが続きます:

First GameBCには、実際にはUserMembershipをパラメーターとして取得するCreate()メソッドがあります。 Create(UserMembership)。次に、UserMembershipエンティティを介して、どのようなメンバーシップとユーザーであるかを把握します。承認されると、ゲームが作成されます。例外がスローされない場合、またはゲームが壊れたルールメッセージを受け取る場合は、クライアントに通信する方法に依存します。ドメインの知識を漏らすことなく、アプリケーション層で調整を行うことができます。

2番目他の答えの1つとして行います。 Game.Create(UserId)メソッド内でCreateGameEventを発生させます。そのイベントは、アプリケーションレイヤーに存在するEventHandler(アプリケーションの起動時にIoCによって登録されます)によってキャッチされ、リポジトリーを介してUserMembershipを検索します。ドメイン知識の小さな漏洩は、アプリケーション層で検証されることを許可されている人を知っているビジネスルールです。これは、CreateGameEventHandlerにUserIdとRuleRef(文字列 "CAN_CREATE_GAME"または列挙型)を取得させ、UserPermissionオブジェクトに権限を確認させることで解決できます。そうでない場合。例外がスローされ、アプリケーション層でキャッチされます。欠点は、権限参照文字列をCreateメソッドでハードコード化したいということです。

番目 ... 2番目のアプローチが終了するところに続きます。 SRPの原則に従っている場合、GameBCがユーザー権限の検索を行うのに適していない可能性があることを知っています。しかし、アクションは何らかの方法でそのメソッドを中心にトリガーされます。別の方法として、Create(GroupLeaderユーザー)があります。 OR Game.Create(User user)を使用して、UserがGroupLeader型であることを確認します。Create(GroupLeader)は、このメソッドを呼び出す必要があることを通知します。

最後多分これを書いているときに私がもっと好きな代替案です。エンティティを作成するときは、通常、そのCreate(Save)メソッドをリポジトリに配置します。 IGameRepositoryインターフェイスは、ドメインアセンブリプロジェクトのゲームエンティティの横にあります。ただし、ゲームエンティティのライフサイクルの開始を担当するGameFactoryを作成することもできます。ここにもCreateメソッドを配置するのに適した場所があります... GameFactory.Create(GroupLeader){return new Game.OwnerUserId = GroupLeader.Id;次に、IGameRepository.Save(Game)を保存します。

次に、「ゲームを作成するにはGroupLeaderインスタンスが必要です」と他の開発者に伝える直観的で自己記述的な方法があります。

最後に、ドメインを知っていて、自分に最も適したものを見つけ出すことを理解してほしい。実用的であり、エリック・エヴァン・ハードコアに行かないでください。物事がどのように行われるかについて「宗教」に行き詰まっている開発者は非常にたくさんいます。プロジェクトの規模、お金、時間、および他のシステムへの依存性なども、DDDを実行する際の厳格さに影響を与えます。

幸運を。

2
Magnus Backeus

ユーザーの役割とユーザー情報をGame bounded context serviceに渡し、ゲームBC内にGroupLeadervalue objectを含めることをお勧めします。これにより、いつ誰がgroup_leaderかを常に知ることができます。

0
Asad Ali