web-dev-qa-db-ja.com

永続化メソッドに戦略パターンを使用すると、ビジネスオブジェクトにCRUDメソッドを含めることが「大丈夫」になりますか?

まず、私はこれを純粋に学術的な演習として求めています。 レポジトリパターンを使い続けるだけでとても満足しています。

「永続化メソッドをビジネスオブジェクトに配置するのは、ビジネスロジックの不可欠な部分だったので、永続化メソッドを配置した」と聞いたことがあります。これは、ビジネスオブジェクトにTimeToSaveイベント

ただし、永続化メソッドに戦略パターンを使用する場合、技術的にはドメインレイヤーに永続化ロジックを含めないことを主張することもできます。以下の例をご覧ください。

class MyEntity
{
     private ISaveMyEntity _saver;
     public MyEntity(ISaveMyEntity saver) { _saver = saver; }

     public void Save()
     {
         _saver.Save(this);
     }

     //Real business methods
}

ビジネスオブジェクトに含めると、独自の永続化メソッドが便利です。さらに、ビジネスパターンがDTOになる一方で、リポジトリパターンは悪用されることが多く、CRUDだけでなくすべてのビジネスロジックが含まれることになります。私の意見では、これはCRUDメソッドを含むビジネスオブジェクトよりも悪いです。

戦略パターンを介してビジネスオブジェクトに永続性へのアクセスを含めることの潜在的な落とし穴は何でしょうか?戦略パターンを使用すると、ビジネスオブジェクトのストレージコードに一般的に関連するいくつかの問題が緩和されますか?

5
TheCatWhisperer

ビジネスルールを含む、ビジネスロジックを表す値オブジェクトを取得しました。次に、それらを永続化するメカニズムを追加します。けっこうだ。データベースを読み取ってデータベースから構築するメカニズムを追加してみませんか?おそらく、静的なLoadメソッドがIGetMyEntityインスタンスをパラメーターとして使用しますか?

あなたがそれをしている間、おそらくいくつかのメソッドを追加するのが最善です:

  • RenderAsHtml
  • ToString
  • ToJson
  • ToXml

これでどこへ行くのかわかる?それはすべて単純なSaveメソッドで始まりますが、いくつかのコミットは後でオブジェクトが突然すべてを認識し、凝集度が低く、すべてが単一の場所に詰め込まれ、不要なボイラープレートの残りをドラッグせずに再利用することは不可能ですそれ。

リポジトリー・パターンは拡張性が高いため、非常に一般的になりました。話しているオブジェクトが実際にビジネスルールのメモリ内表現であると想定されている場合、そのAPIの一部としてCRUDメソッドを含めることはできません。

DTOについてのあなたの意見を拡大するために、何らかの操作を行っているときに、ビジネスルールの検証が必要な場所はどこですか?読み取り中ですか?おそらくそうではない。一部のリソースへのアクセスを禁止することもできますが、それはビジネスルールというよりはむしろ承認の問題です。

ビジネスルールを適用する可能性が高い場所は、データがシステムに入力されるときです(完全に新しいものか、更新されたものか)。その場合、システムの状態を変更する操作が有効であることを確認する必要があります。実行される場合があります。このためには、制約を適用する値オブジェクトまたはエンティティを使用する必要があります。リポジトリは、実際にはビジネスロジックを格納する場所ではありません。リポジトリの目標は、ビジネスルールを適用することではなく、オブジェクトの永続性を抽象化することです。リポジトリに渡されるオブジェクトは、さらに処理するためにリポジトリに渡される前に、オブジェクト自体が有効であることを確認する必要があります。

また、リポジトリをすべてのCRUD操作を知っているクラスとしてではなく、アプリケーション内のレイヤーのように見ることをお勧めします。アプリケーションが大きくなると、読み取り時にエンティティの複数の表現が必要になる可能性があります。複数のGet*メソッドを持つ単一のクラスとしてリポジトリがある場合、これは適切な感じではなく、クラスは非常に大きくなり、すぐに保守することが困難になる可能性があります-個人的な経験によるものです。

8
Andy

DTOが永続化に使用される理由はたくさんありますが、おそらく最も厄介なのはデータベーストランザクションです。 (つまり、複数のステップが必要であり、単一のきちんとしたUPDATEINSERTDELETEなどを実行できない場合)

トランザクションの基本的な要件のいくつかは次のとおりです。

  • 単一のきちんとしたSQLステートメントで複数のステップを簡単に実行することはできません
  • すべてのステップが成功するまで、COMMITは起こりません
  • ステップが失敗した場合、ROLLBACKが発生してトランザクション全体が取り消され、データベースは以前の状態のままになります。部分的な更新や孤立したレコードはありません。

アプリケーション内のビジネスオブジェクトを検討します。

  • ビジネスオブジェクトは、アプリケーションの動作をサポートします
  • 彼らの寿命は必然的に彼らの行動にリンクされます
  • データベースに反映されるべきではない状態が含まれている可能性があります

次に、すべてのデータがデータベースからビジネスオブジェクトに直接取り込まれるシナリオを考えます。複数のクエリとUPDATE/INSERT/DELETEステートメントを必要とする可能性のある複雑なトランザクションをどのように実行しますか?

  • トランザクションロジックはどこに配置しますか?複数のビジネスオブジェクトが関係している場合は、これらのオブジェクトのいずれにも属していないことは明らかです。
  • 前の箇条書きに関連して、コミットまたはロールバックが複数のビジネスオブジェクトからのデータに影響を与える場合、どのオブジェクトがコミットまたはロールバックを担当しますか?
  • データベースで多対多として表されるビジネスオブジェクト間の関係のトランザクションをどのように処理しますか? (例:Student-Teacher)
  • トランザクションがキャンセルされ、ロールバックが必要になった場合はどうなりますか?ドメインオブジェクトは動作に関連付けられているため、単に破棄するだけでなく、失敗したトランザクションのデータが含まれています。
  • ドメインオブジェクトにキャッシュされていたデータがデータベースと同期しなくなった場合はどうなりますか?

重要な問題は次のとおりです:ビジネスオブジェクトは一時的ではありません。それらにデータを追加すると、それらのdataが暗黙的にそれらの一部になりますstateそのデータをキャッシュしているため、キャッシュの存続期間はビジネスオブジェクトの存続期間と同じです。


なぜDTOはこれを解決するのですか?

  1. ビジネスオブジェクトがデータにDTOを使用する場合、データはその状態の一部ではありません。同様に、ビジネスオブジェクトは複数のDTOからのデータを必要とする場合がありますが、これも問題ありません。
  2. DTOはその性質上、一時的なオブジェクトです。それらの存続期間は、アプリケーションの動作とは関係ありません。
  3. ビジネスオブジェクトは、そのデータのDTOの有効期間を気にする必要はありません。 1つが破棄された場合、必要なのは、データベースへの新しいクエリを実行して、光沢のある新鮮でクリーンな新しいクエリを取得することだけです。
  4. トランザクションデータはドメインオブジェクトの一部ではないため、ドメインオブジェクトの状態を変更することなく、DTOを使用してトランザクションを作成できます。
  5. トランザクションが失敗した場合は、すべてのDTOを破棄します(これらはいずれにせよ一時的なオブジェクトであり、成功すると破棄されます)。ビジネスオブジェクトは、潜在的に無効な状態のままになることを心配する必要はありません。
  6. アプリケーションは、キャッシュによるパフォーマンス上のメリットがあるかどうかに基づいて、キャッシュするデータを選択できます。 「すべて」のデータがデフォルトでキャッシュされる代わりに。
  7. ビジネスオブジェクトごとに、個別のキャッシュ無効化やデータベースとの再同期を実装する必要はありません。
  8. ドメインオブジェクトによってデータがキャッシュされていない場合、そのデータに関連する動作は常にデータベースから直接クエリされるため、最新のデータに対して確実に機能します。
  9. 最後に、複数のエンティティを含むトランザクションロジックを記述する必要がある場合、ビジネスオブジェクトがデータに関連付けられなくなったため、どのビジネスオブジェクトが属するかについて心配する必要はありません。

結論-行動ドメインオブジェクトのlifetimeがデータベースに読み書きするオブジェクトの寿命を決定すると、コードの編成、トランザクションのロールバック、キャッシュの無効化、およびデータの同期の問題が発生します。

ダイアグラムが明確なビジネスオブジェクトでいっぱいになっていると「クリーン」に見えますが、それぞれが強力なIDを持ち、問題ドメインに簡単に関連付けられ、独自のデータと動作のすべてを1か所にカプセル化しますが、現実マルチオブジェクトの相互作用と、外部データベースでの永続データの管理の複雑さは、完全ではありません。

6
Ben Cottrell

これにより、永続化が必要ない場合にオブジェクトを使用するのが不便になる可能性があると思われます。単体テストを行うには、永続化インターフェースをモックする必要があります。イベントベースのシステムでは、SaveMeイベントにサブスクライブしないことを選択できます。

別の潜在的な問題は、この方法で処理を行うと、効率的なバッチCRUD操作を実行する場合に、処理が困難になる可能性があることです。

3
TheCatWhisperer