5

DDD Evansを読み、C#とEntity Framework 4.1+LINQを使用して集約ルートリポジトリの設計を実験しています。

ただし、DBに送信される実際のクエリが心配です。私はSQL2008R2を使用しており、SQL Profilerを実行して、LINQコードに応答してDBが何を行っているかを調べています。

PersonとEmailAddressを使用した単純な2エンティティの設計について考えてみます。1人の人は0から多数のEmailAddressesを持つことができ、EmailAddressは正確に1人の人を持つ必要があります。Personは集約ルートであるため、電子メールアドレスのリポジトリは存在しないはずです。電子メールアドレスは、Personリポジトリから選択する必要があります(DDD Evansによる)。

比較のために、メールアドレス用に一時的なリポジトリを設定しています。次のコード行:

var emailString = "someone@somewhere.com";
var emailEntity = _tempEmailRepository.All.SingleOrDefault(e => 
    e.Value.Equals(emailString, StringComparison.OrdinalIgnoreCase));

...プロファイラーに従ってクリーンなSQLクエリを実行します。

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Value] AS [Value], 
[Extent1].[IsDefault] AS [IsDefault], 
[Extent1].[IsConfirmed] AS [IsConfirmed], 
FROM [dbo].[EmailAddress] AS [Extent1]

次のコードを使用して、個人リポジトリから電子メールを選択できます。

var emailEntity = _personRepository.All.SelectMany(p => p.Emails)
    .SingleOrDefault(e => e.Value.Equals(emailString, 
        StringComparison.OrdinalIgnoreCase))

これにより、実行時に同じエンティティが取得されますが、SQLプロファイラーに異なるコマンドが表示されます。

SELECT 
[Extent1].[Id] AS [Id],  
[Extent1].[FirstName] AS [FirstName],  
[Extent1].[LastName] AS [LastName], 
FROM [dbo].[Person] AS [Extent1]

Personから選択する上記のクエリに加えて、DBのEmailAddress行ごとに1つずつ、多数の「RPC:Completed」イベントがあります。

exec sp_executesql N'SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Value] AS [Value], 
[Extent1].[IsDefault] AS [IsDefault], 
[Extent1].[IsConfirmed] AS [IsConfirmed], 
FROM [dbo].[EmailAddress] AS [Extent1]
WHERE [Extent1].[PersonId] = 
    @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

exec sp_executesql N'SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Value] AS [Value], 
[Extent1].[IsDefault] AS [IsDefault], 
[Extent1].[IsConfirmed] AS [IsConfirmed], 
FROM [dbo].[EmailAddress] AS [Extent1]
WHERE [Extent1].[PersonId] = 
    @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=2

私のテストデータベースにはdbo.EmailAddressに14行あり、14個の異なるRPC:Completed呼び出しがあり、それぞれが異なる@EntityKeyValue1値を持っています。

dbo.EmailAddressテーブルがより多くの行を取得すると、これらのRPCの多くがデータベースで呼び出されるため、これはSQLのパフォーマンスに悪いと思います。EF 4.1 + LINQでDDD集約ルートリポジトリを使用するための別のより良いアプローチはありますか?

更新:解決済み

問題は、Allプロパティがを返していたことでしたIEnumerable<TEntity>。これがに変更された後IQueryable<TEntity>、LINQが起動し、Person+Email全体を一度に選択しました。ただし、AllからIQueryableを返す前に、.Include(p => p.Emails)をチェーンする必要がありました。

4

1 に答える 1

12

現代のORMがすでに提供している抽象化のレベルを考えると、私は個人的に、あなたとあなたのデータベースの間に抽象化レイヤーを追加しないようにアドバイスしたいと思います。車輪の再発明に加えて、サービスレイヤーで選択したORMを直接使用すると、クエリ、フェッチ、およびキャッシュ戦略をよりきめ細かく制御できるようになります。

AyendeのシリーズWagesofSinは、特にLINQがすでに必要と思われるほとんどすべてのものを効果的に提供していることを考えると、最新のORMで仕様/リポジトリを使用することに反対する他のさまざまな議論の良いリソースです。

私は過去のプロジェクトで「DDD」のルートをたどりました(それは当時私が持っていたDDDの理解に結びついているので引用符で囲んでいます)。後から考えると、公開討論でDDDがこれらのパターンを適用することにしばしば縮小されるのは残念だと思います。私はその罠に陥りました、そして私は他の人がそれを避けるのを助けることができることを願っています。

リポジトリと仕様はインフラストラクチャパターンです。インフラストラクチャは、それ自体が目的ではなく、目的を果たすためにあります。インフラストラクチャに関しては、再利用された抽象化の原則を厳密に適用することをお勧めします。簡単に要約すると、RAPは、2人以上のコンシューマーによって消費され、抽象化の追加レイヤーが実際に何らかの動作を実装する場合にのみ、抽象化を導入する必要があると述べています。何か(ORMなど)からあなたを切り離すために抽象化を導入するだけの場合は、非常に注意してください。そうすると、抽象化が漏れてしまう可能性があります。

DDDの要点は、ドメインモデルをインフラストラクチャから分離し、ドメインモデルを可能な限り表現力豊かにすることです。リポジトリを使用せずにこれを達成できないという証拠はありません。リポジトリは、データアクセスの詳細を隠すためだけにあります。これはORMがすでに行っていることです。(ちなみに、DDDの本の時代を考えると、ORMの一般的な使用法は当時の写真にはなかったと思います)。さて、リポジトリは集約ルートなどを強制するのに役立つかもしれません。しかし、これは「読み取り」操作(クエリ)と「書き込み」操作(コマンド)を明確に区別することによって処理する必要があると思います。ドメインモデルが関連しているのは後者の場合のみであり、クエリは多くの場合、調整された(そしてより柔軟な)モデル(DTOや匿名オブジェクトなど)によってより適切に処理されます。

仕様の場合も同様です。仕様の意図された目的は同様です。それらの力は、オブジェクトをクエリするためのドメイン固有言語の要素を構築することにあります。これらの要素を組み合わせるために提供された一般化された仕様パターンの「接着剤」の多くは、LINQの出現により廃止されました。ヒント:述語ビルダー(<50行のC#)を見てください。仕様を実装するために必要なのは、おそらくすべてです。

この長い(そしてうまくいけばあまり混乱していないので、後で再訪したいと思います)投稿を要約すると:

  1. インフラストラクチャに夢中になるのではなく、インフラストラクチャを構築していきます。
  2. ビューをサポートするためではなく、ドメイン固有の動作にドメインモデルを使用します。
  3. DDDのより重要な部分に焦点を当てます。集約ルートを使用し、ユビキタス言語を構築し、ビジネスの専門家との良好なコミュニケーションを確保します。
于 2011-05-18T20:15:43.903 に答える