私はASP.NET Webサイトアプリケーションを継承しました。以前の開発者は、DDDの概念の一部であると私が信じているものを使用しました。私はDDDを初めて使用するので、N層のトランザクションスクリプトのバックグラウンドから来たDDDの実用的な側面に苦労していることを認めなければなりません。
このサイトの問題の1つは、速度が遅いことです。巨大なグリッドのページングはなく、ビジネスロジックレイヤーは、90%の操作に必要なデータよりもはるかに多くのデータをプルダウンします。70%の操作は、データをグリッドにバインドするための読み取りです。
一般的な設計では、ビジネスロジックレイヤーには集約ルートごとにServiceクラスがあり、その上にさまざまな「GetByX()」、「FindBestMatching()」、および「Update()」タイプのメソッドがあります。これらのServiceクラスは、 「組織」や「ユーザー」などのドメインオブジェクトが作成されます。
私の快適なトランザクションスクリプト時代に戻って、私のサービスレイヤーは同様でした:おそらくいくつかのロジックがあり、リポジトリへの呼び出し(またはEF 6は、テストのためにモックできるため、EFを直接使用するため)であり、その後、呼び出しが返されます。返されたオブジェクトは、おそらくいくつかのPOCOエンティティオブジェクトを含む、関連する戻りデータを含むDTOタイプのオブジェクトです。これらのDTOは、Webサービスを介してクライアントに送信され、ASP.NETまたはSilverlightグリッドにバインドされます...すべて良かったです。
サービスレイヤーでの操作は非常に限定的で、次のようなクエリが可能です。
"GetUsersWithSurnameIncludeChildren(string surname, int skip, int take)"
関連データのページングと積極的なロードをサポートする、ニースのナローDBクエリを可能にします。高速で、実際に必要なデータのみをロードしました。
現在、このサイトで継承したのは、実際のOOビジネスドメインのモデルです。これらのクラスはPOCOであり、EFから生成されたものではありませんが、最初にリポジトリとしてデータベースが使用されます(ドメインオブジェクトとEFオブジェクト間の多くのマッピング以下にドメインオブジェクトの例を示します(ロジックなし)。
public class Organisation
{
public int OrganisationID { get; set; }
public string Name { get; set; }
public IList<Organisation> AssociatedOrganisations { get; set; }
public IList<User> Staff { get; set; }
public IList<User> Managers { get; set; }
// Business logic method implemented below here
}
クラスの多くは基本的にエンティティフレームワークオブジェクトのコピーであり、他のクラスには大量のロジックがカプセル化されています。
現在、サイトが機能する方法は、OrganizationServiceサービスクラスが新しい組織ドメインオブジェクトを作成するたびに(また、1つのヒットでコレクションを作成する可能性があります)、そのオブジェクトのすべてのフィールドとコレクションにデータを入力します...それらのオブジェクトのフィールドとコレクション!巨大なディープツリーがメモリ内に作成され、すべてのデータを取得するには、結合とユニオンのスタック(EFによって生成)が必要です。これは、フラットグリッドの負荷を主に表示するWebサイトにはまったく不適切です。
私の質問は:
1)70%が表示用にDBからデータを引き出し、30%がビジネスロジックであるアプリケーションは、実際にはDDDの候補として不適切ですか?
2)DDDが適切である場合、このようなシナリオは一般的にどのように設計されていますか?ページとグリッドにバインドするためのフラットデータの負荷を引き出すためだけに設計された個別の "クエリサービス"を実装し(これらはEF POCOS自体である可能性があります)、ロジックが必要なアプリケーションの部分のためだけにドメインオブジェクトを予約しますか?実際に行われますか?
3)実際にアクセスされたときにデータをドメインオブジェクト参照型とコレクションにのみロードする必要がありますか(EF遅延ロードのように...)?私が読んだ以前の投稿では、ドメインオブジェクトでの遅延読み込みはコードの匂いであると示唆されています: DDD遅延読み込みコードの匂い
今夜DDDの本を購入しようとしています:)
Evansの本を読むと、DDDが概念のコレクションであることがわかります。本は10年以上前に書かれました。今日の私の意見は、それは良い部分を持っていますが、簡単に誤解され、時には不適切に使用されているということです。実際には、アプリケーションや選択したツールに必要ない、または適切でない一部のパーツがあります。
EFに関する限り、エンティティのデータと動作を組み合わせるというDDDの概念は、それとうまく融合するとは思いません。予期しないエラーやパフォーマンスのボトルネックの多くを回避したい場合は、EFエンティティモデルに動作がないようにする必要があります。ここで他の回答が指摘しているように、CQ(R)Sパターンを使用することは、EFエンティティーの外部で動作を実装する良い方法です。要するに、EFエンティティのメソッドを使用して「リッチドメインモデル」を実装しないようにしてください。ビジネス知識を「モデル」にまとめることはできますが、「モデル」にはデータ(エンティティ)と動作(操作)の異なるクラスを含める必要があります。
リポジトリの考え方も好きではありませんが、それは主に私の意見であり、EFのようなORMを使用している場合は本当に必要ないというのであれば、他の人も反対するでしょう。 10年前のことかもしれませんが、現代のジェネリック医薬品が登場する前は、より有用でした。最近では、EF DbContextを単一の汎用インターフェースでラップし、単一の汎用リポジトリー(および作業単位)を使用できます。
一方、ユビキタス言語、集約ルート、制限付きコンテキスト、クエリ基準などのようなものです。私は真珠だと思います。アプリケーション層を薄く保つことも、時代を超えて良い習慣です。他の開発者が適切なDDDの概念を適用していない可能性があり、ここの他の誰かがすでに述べたように、解決した問題よりも多くの問題が発生したようです。
私はいくつかのDDDモデルを構築しましたが、永続性はNHibernateによって処理されました。初期のプロジェクトではリポジトリパターンを採用し、すべてのクエリをリポジトリに組み込みました。
これは理論的には素晴らしいアイデアですが、実際に学んだことは、リポジトリの実装時に大量のボイラーの場所を作成してしまうことであり、また、リポジトリは実際には役に立たないということです。標準のsave、delete、FindByIDメソッド以外に、使用される実際のクエリは、ほとんどの場合、特定のユースケースに固有です。特に、ロードするプロパティを考慮に入れる場合はそうです。
そのため、引き続きリポジトリパターンを使用しますが、save、delete、findByID、findByQueryメソッドのみを使用します。クエリはオブジェクトにカプセル化され、リポジトリに渡されます。クエリオブジェクトはアプリケーション層に組み込まれています。
1)そうは思いません。 DDDは複雑なシステムに適しています(つまり、CRUDよりも高度なものを意味します)。ドメインロジックは、クエリロジックと比較して、システム内でのシェアに関係なく非常に複雑になる可能性があります。
2)これに対処する方法は2つあります。 1つは、集約サイズの制限です。小さなアグリゲートは、肥大化し、データの読み込みが遅くなる傾向があります。もう1つは、読み取りと書き込み(CQRS)を分離したものです。これは基本的にあなたが言及している「クエリサービス」ですが、エンティティではない個別の読み取りモデルオブジェクトを返すようにすることをお勧めします。
3)コードのにおいを遅延読み込みするエンティティの呼び出しは、やや...それは実際に解決策になる可能性があります。ただし、巨大なコレクションに1つのエンティティを追加して、このコレクション全体をロードしなければならないなど、パフォーマンスの問題が依然として発生する場合があります。より良い集合設計はこれに対処できます。
コードがあなたが苦労しているものである場合、それがそこにある理由を見つけてください。有効な理由が見つからない場合は、コードを削除して、そこに存在する理由があるコードを記述します。あなたの質問は、いくつかの問題を解決したかもしれないが、もっと多くの技術的な問題をもたらしたパターンの使用によって引き起こされた自発的な問題のリストを示しています。
SO DDDが時間の無駄であるかどうかについての私の答えを参照させてください: https://stackoverflow.com/questions/810606/is-ddd-a-時間の浪費/ 810661#810661
これにより、DDDとは何か、何でないかが少しわかります。 TL; DRは、それが技術的な解決策ではないが、ある種の技術的な解決策になる可能性がある(そうはならない!)ことですが、そうする必要はありません。
広範なグラフのフェッチは、関連する要素のコードをフェッチする遅延ロードまたはループを配置することにより、SELECT N + 1の問題を示唆する可能性があります。これは、メモリ内のデータのプロジェクションを生成できるようにするために行われる場合があります。ただし、クラススニペットを見ると、DDDに深い影響は見られません。コレクションがなければ影響がなかったからです。
モデル内のエンティティに対するlinqクエリとして必要なデータの予測を公式化してDBに実行させる方がはるかに高速になる可能性があるため、グラフ内のエンティティインスタンスを実際にフェッチする必要があるかどうかを確認するのが賢明かもしれません。セット生産。結果セットは、新しいPOCOオブジェクトにフェッチできます。これは、単純なフラットリストであり、UIで使用されます。これはもちろん読み取り専用データですが、多くの場合これで十分です。