集約ルート内のエンティティの変更の検出
-
07-07-2019 - |
質問
私は、集合体の一部であるエンティティの変更を検出するために人々が取ったかもしれないアプローチを探しています。私はうまくいくものを持っていますが、私はそれについて夢中ではありません。基本的に、私のリポジトリは、集約ルートの状態が変化したかどうかを判断する責任があります。集約内に Book
という集約ルートと Page
というエンティティがあると仮定しましょう。 Book
には、 Pages
コレクションに保存された1つ以上の Page
エンティティが含まれています。
主に、挿入と更新のシナリオは、集約ルートとそのエンティティを検査してキーの存在を判断することにより行われます。キーが存在する場合、オブジェクトは一度に基礎となるデータソースに保存されていると推定されます。これにより、更新の候補になります。しかし、それはエンティティにとってそれだけに基づいた決定的なものではありません。集約ルートでは、答えは明らかです。1つしか存在せず、それが特異なエントリポイントであるため、キーの存在が操作を決定すると想定できます。私の場合、変更日を取得できるように集約ルート自体を再度保存することは受け入れられるシナリオです。
エンティティ自体のこの動作を容易にするために、 EntityBase
クラスには2つの単純なプロパティが含まれています: IsUpdated()
、 IsDeleted()
。どちらもデフォルトはfalseです。前述のように、キーの存在に基づいてその決定を行うことができるため、それが新しいかどうかを知る必要はありません。実装のメソッド(この場合はページ)には、バッキングデータセット IsUpdated()
をtrueに変更する各メソッドがあります。
たとえば、Pageには UpdateSectionName()
というメソッドがあり、読み取り専用の SectionName
プロパティのバッキング値を変更します。このアプローチは一貫して使用されます。これは、そのデータ設定を実行するメソッド(エンティティーが無効な状態に入ることを防ぐ)でバリデーターの論理接続点を許可するためです。最終的に、メソッドの最後に this.IsUpdated()= true;
を追加する必要があります。
集約ルートが Save()
のリポジトリに送信されるとき( Insert()
または Update()へのロジックスイッチ) code>操作)、次に、
Book
の Pages
コレクションを反復処理して、次の3つのシナリオのいずれかを持つページを探します:
- キーなし。キーのない
Page
が挿入されます。 -
IsDeleted = true;
削除は更新より優先され、削除はコミットされます-Page
の更新は無視されます。 -
IsUpdated = true;
ページの更新がコミットされます。
この方法で行うと、Pagesコレクション内のすべてを盲目的に更新することを防ぐことができます。たとえば、Bookに数百のPageエンティティがある場合、気が遠くなる可能性があります。私は本のコピーを取得して比較を行い、検出された変更のみをコミットすることを考えていました(存在および/または比較に基づいて挿入、更新、削除)が、それはそれについて行くにはひどくおしゃべりな方法のようでした。
主な欠点は、開発者がエンティティの各メソッドでIsUpdatedを設定することを忘れないでください。忘れてください。その値の変更を検出することはできません。変更を透過的にタイムスタンプできるカスタムバッキングストアのアイデアをいじくりました。これにより、 IsUpdated
がリポジトリが更新を集約するために使用できる読み取り専用プロパティになります。
リポジトリは、集約ルートが追加されたときに生成されたタイムスタンプに基づいて動作する作業単位パターンの実装を使用しています。複数のエンティティキューが存在する可能性があるため
解決
要するに、私の答えは、私が提案したものを採用したということです。改善の余地があると確信していますが、それは機能しています。変更には実際にはほとんど時間がかからなかったので、この場合はKISSまたはYAGNIプリンシパルからあまり遠くに移動しなかったように感じます。 :-)
私はまだ、操作に関するタイミング関連の問題の余地があると感じていますが、リポジトリの実装でそれらを回避できるはずです。理想的な解決策ではありませんが、修正に要する時間よりも短い時間で回避できる問題を修正するために車輪を再発明する価値があるかどうかはわかりません。