1

あなたがドメインオブジェクトがあるとしましょう:Entity FrameworkのとDDD - ロードビジネス層にエンティティを渡す前に、関連するデータを必要と

class ArgumentEntity 
{ 
    public int Id { get; set; } 
    public List<AnotherEntity> AnotherEntities { get; set; } 
} 

をそして、あなたはそれに対処するためのASP.NETのWeb APIコントローラーがあります。

[HttpPost("{id}")] 
public IActionResult DoSomethingWithArgumentEntity(int id) 
{ 
    ArgumentEntity entity = this.Repository.GetById(id); 
    this.DomainService.DoDomething(entity); 
    ... 
} 

エンティティ識別子を受け取り、idでエンティティをロードし、ドメインサービスでビジネスロジックを実行します。

問題: 問題は関連するデータです。 ArgumentEntityにはAnotherEntitiesコレクションが含まれています。これは、Include/Loadメソッドを使用して明示的に要求する場合にのみ、EFによって読み込まれます。 DomainServiceは、ビジネス層の一部であり、永続性、関連するデータおよびその他のEFの概念については何も知らないでください。サービスメソッドをDoDomething

は、ロードされたAnotherEntitiesコレクションでArgumentEntityインスタンスを受信することを期待します。 これは簡単です。必要なデータをRepository.GetByIdに含めて、関連するコレクションでオブジェクト全体を読み込みます。

は今大規模なアプリケーションの現実に簡略化した例から戻ってくることができます:

  1. ArgumentEntityははるかに複雑です。それには複数の関連するコレクションが含まれており、関連するエンティティにも関連するデータがあります。

  2. DomainServiceの複数の方法があります。各方法は、ロードされる関連データの異なる組み合わせを必要とする。

私は可能な解決策を想像することもできますが、それらのすべてが理想からかけ離れている:

  1. は常に全体のエンティティをロード - >しかし、それは非効率的としばしば不可能です。

  2. は、いくつかのリポジトリメソッド追加:GetByIdOnlyHeader、GetByIdWithAnotherEntities、GetByIdFullDataコントローラに特定のデータ・サブセットをロードするために - >が、コントローラがロードおよび各サービスメソッドに渡すためにどのデータに気づきます。 - >それは各サービスメソッド呼び出しのために非効率的なSQLクエリでGetByIdOnlyHeader、GetByIdWithAnotherEntities、GetByIdFullData各サービスの方法で特定のデータのサブセットをロードする:

  3. は、いくつかのリポジトリのメソッドを追加します。 1つのコントローラアクションに対して10のサービスメソッドを呼び出すとどうなりますか?必要な追加データロードする

  4. 各ドメインのメソッド呼び出しリポジトリ方式(例えば:をEnsureAnotherEntitiesLoaded) - 私のビジネス・ロジックは、関連データのEFの概念に気づいので>それは醜いです。

質問: がどのようにビジネス層に渡す前に、エンティティの読み込みに必要な関連データの問題を解決するのでしょうか?

答えて

0

私の例では、明らかにApplication Layerに属しているメソッドDoSomethingWithArgumentEntityが見えます。このメソッドは、データアクセスレイヤーに属するRepositoryを呼び出しています。私はこの状況が古典的なLayered Architectureには合致しないと考えています.DALをApplication Layerから直接呼び出すべきではありません。

だからあなたのコードは、別の方法で書き換えることができます:あなたはそれは、この特定の操作に必要なものは何でもレポから読み取ることができます

[HttpPost("{id}")] 
public IActionResult DoSomethingWithArgumentEntity(int id) 
{ 
    this.DomainService.DoDomething(id); 
    ... 
} 

実装DomainServiceで。これにより、アプリケーション層でのトラブルを回避できます。ビジネスレイヤーでは、読み込みをより自由に実装することができます。サーバーリポジトリメソッドでは、ハーフフルエンティティまたはEnsureXXXメソッドなどを読み込みます。あなたが操作のために読む必要があることに関する知識は、操作のコードに置かれ、アプリケーション層でこれ以上の知識は必要ありません。

このような状況が発生するたびに、あなたのエンティティについての強いシグナルがあらかじめ設計されていません。 krzysは、企業は粘着的な部分を持っていないと述べた。つまり、エンティティの一部が別々に必要な場合は、このエンティティを分割する必要があります。

+0

私は明確にすべきだと思います:リポジトリはここでは抽象ですそれはタイプIRepositoryであり、DIを介して注入される。リポジトリインタフェースはビジネスロジックの一部であり、リポジトリ実装はDALの一部です。したがって、DALはアプリケーション層から呼び出されません。問題1:ビジネス層にエンティティの代わりに識別子を渡すことは、しばしば悪い習慣とみなされます。問題2:DoSomethingとDoSomethingElseを同じコ​​ントローラで呼び出す必要がある場合その場合、両方のメソッドがエンティティをロードして、1つではなく2つのSQLクエリを使用します。 –

+0

私はあなたの見解を理解していますが、論理リポジトリインターフェイスは永続性の抽象であるため、論理的にはDALに属します。別の問題を考えてみましょう。アプリケーションコードの単体テストを書くときには、ドメインサービスだけでなくリポジトリもスタブします。問題1について:たぶん、私の経験は異なります。問題2:ドメインの問題ではなく、インフラの問題です。インフラストラクチャ(DAL)は、複数の負荷が目に見える問題になった場合に、いくつかのキャッシングを実装できます。 –

0

ニース質問:)

私は自分自身の「関連データ」とは、厳密なEFの概念ではないことを主張するだろう。関連データは、NHibernate、Dapper、またはストレージにファイルを使用する場合でも有効な概念です。

私は他の点にほとんど同意しますが、ここに私が通常行っていることは次のとおりです:あなたのケースではGetByIdという1つのリポジトリメソッドがあります。このメソッドにはidとparams Expression<Func<T,object>>[]という2つのパラメータがあります。そして、の中にのリポジトリが含まれています。この方法では、ビジネスロジックのEFに依存することはありません(必要に応じて別のタイプのデータストレージフレームワークに対して手動で式を解析できます)。各BLLメソッドは、実際に必要な関連データを自分で決定できます。

public async Task<ArgumentEntity> GetByIdAsync(int id, params Expression<Func<ArgumentEntity,object>>[] includes) 
{ 
    var baseQuery = ctx.ArgumentEntities; // ctx is a reference to your context 
    foreach (var inlcude in inlcudes) 
    { 
     baseQuery = baseQuery.Include(include); 
    } 
    return await baseQuery.SingleAsync(a=>a.Id==id); 
} 
+0

あなたの経験を共有してくれてありがとう。必要な関連データを持つエンティティをロードするために、BLLメソッドと各BLLメソッド呼び出しリポジトリに識別子を渡すことを意味しますか? –

+0

基本的にはyesです。 「IDを渡す」とは、BLLメソッドに実際のパラメータがあることを意味することもあります。時には、BLLメソッドがある種の注入サービスを通じてIDにアクセスすることもあります。しかし、一日の終わりには、各BLLメソッドはIDを何らかの形で認識してから、どのプロパティをロードする必要があるのか​​を自分で決定し、それに応じて式をリポジトリメソッドに渡します。 –

0

DDDのコンテキストでは、この問題の原因となったプロジェクトでモデリングの面を見逃していたようです。あなたが書いたエンティティは、非常に粘着的ではないように見えました。異なるプロセス(サービスメソッド)に関連する異なるデータが必要な場合は、適切な集計がまだ見つかりませんでした。エンティティを複数の集約に分割することを検討してください。特定のAggregateに関連付けられたすべてのプロセスには、このAggregateに含まれるすべてのデータまたはすべてのデータが必要です。

私はあなたの質問の答えは分かりませんが、あなたがモデルをリファクタリングすることができれば、そのような問題に遭遇することはないと信じています。

+0

これは答えではありませんが、それは近いです。集約を分割すると、集約間のリンクで別の問題が発生します。しかし、私は[ここに別の質問](https://stackoverflow.com/questions/4919687/aggregate-root-references-other-aggregate-roots)で受け入れ可能な解決策を見つけた –

関連する問題