ドメイン モデルはエンティティ間の関係を定義し、集約ルートを定義してカプセル化とトランザクション境界を提供します。よく知られている関係は、1 対 1 の関係 (エンティティまたは値オブジェクトが集約ルートに含まれる)、1 対多の関係 (集約ルートに子オブジェクトのコレクションが含まれる)、および多対多の関係です。集約ルート間の多対多の関係により、トランザクション境界で問題が発生するため、後者は困難です。したがって、多くの場合、多対多の関係の 1 つの方向がより重要であると見なされ、その関係のみが 1 対多の関係としてモデル化されます。
では、さらに一歩進んでください。ネットワーク。同等のパートナー間の多対多の関係。集約ルートのトランザクション境界に違反することなく、どのようにモデル化できますか?
この広く適用可能な例を見てください:
ノードを持つネットワークがあります。各ノードには限られた数のポートがあります。1 つのポートは、別のノードの 1 つのポートにのみ接続できます。ポートを使用して、ノード間の接続を追加および削除できる必要があります。
これに対する直感的なアプローチは、ポートを含む集約ルートとしてノードをモデル化することです。接続は値オブジェクトのように見え、1 つのポートが 1 つの接続を持つことができます。接続 (ノード A のポート X とノード B のポート Y の間) を集約ルートであるノード A に追加する Node.ConnectTo(nodeId, portId) メソッドを実装できます。ノード A とノード B で 1 回、トランザクションでラップします。ただし、これはトランザクション境界に違反するため、ノード A にのみ保存することにしまし
た。
アプリケーション クライアントでノード B の接続を確認するには、別の読み取りモデルが必要になります。しかし、それは問題ではありません。CQRS アーキテクチャは、これらの可能性を提供してくれます。したがって、接続の追加、削除、表示は問題ありません。
ポートに接続を追加する前に、ポートがまだ空いているかどうかを検証したいときに問題が発生します。トランザクション境界を尊重した結果、(書き込みモデルでは) ポートが既に接続されているという事実が集約ルートには認識されない可能性がありますが、他の集約ルートに格納される可能性があります。
もちろん、クライアントの検証を信頼し、追加先のノードに問題がなければ接続を追加し、一貫性チェックを実行するプロセスに依存して、無効な接続の補正アクションを実行することもできます。しかし、2 つの ConnectTo 呼び出しの周りにトランザクションをラップすることに比べて、それは私にとっては大したことのように
思えます...これにより、集計ルートが間違って選択された可能性があると思いました。. そして、ノードとネットワークを集約ルートとして考え始めました。ここで、ネットワークは接続のコレクションです。ネットワーク集約の良い点は、接続の追加または削除をいつでも検証できることです。ただし、新しい接続によって既存の 2 つのネットワークが結合される場合を除きます。集合体が大きくなり、単一の巨大なネットワークになる可能性があります。実現不可能です。
では、これをどのようにモデル化する必要があると思いますか? 集約ルートをトランザクション境界として尊重し、ネットワークを検証でき、ネットワーク全体を 1 つの集約として保存するリスクを負わないソリューションが見つかりますか? それとも、ここで 3 つの CAP をすべて要求していますが、それは単に不可能なのでしょうか?
2 に答える
わかりました、私はそれについてもう少し読んで考えましたが、これが「正しい」方法だと思います:
- ノード A で ConnectTo メソッドを実行する前に、最終的に整合性のあるビュー モデルをデータ ソースとして使用して、ノード B のポートがまだ空いているかどうかを検証します (これを効率的に検証できないドメイン モデルではありません。上記を参照してください)。
- ConnectTo はノード A でのみ実行されるため、トランザクション境界に違反することはありません。
- ビュー モデルがノード B のポートに接続できない場合は、それが既に使用されているため、真の同時実行例外が発生したため、通知する必要があります。なんらかのアクションを実行する必要があります (手動による介入または自動化されたプロセスのいずれかで検出する必要があります)。通常、この同時実行例外の可能性は非常に低くなります。
ビューモデルは、「何らかの方法で」ドメインモデルに伝播する例外を生成するべきではないため、「新しい方法」には欠陥があると思います。ドメイン モデルは、これを単独で解決する必要があります。
したがって、この場合 (1 対 1 でバインド)、ドメイン モデル内でイベントを利用できるため、
NodeA.connect( "port1" ).to( NodeB ).on( "port3" );
NodeA は「port1」を自分自身で予約します。
NodeA は「portConnectionRequest」を NodeB に送信します。
利用可能な場合、NodeB は「port3」をバインドします。
NodeB は「portConnectionConfirmed」または「portConnectionDenied」を送信します。
NodeA はイベントを受け取り、それに応じて動作します。
上記は、信頼できるメッセージングを前提としています。これは、JVM 内で簡単に実現できますが、分散環境でははるかに困難ですが、それがさらに必要な場合です。信頼できるメッセージング システムが提供されない場合、ビザンチン協定の問題、またはそのサブセットが目の前にあると思います。