2012-06-13 14 views
9

私はDDDの世界ではかなり新しく、その中のいくつかの本(Evans DDD)を読んだ後、私はインターネット上で私の質問に対する答えを見つけることができませんでした。 DDDで子エンティティを作成する方法は?あなたは、インターネット上の多くの情報がいくつかの簡単なレベルで動作していることがわかります。しかし、細部の悪魔は、簡略化のためにDDDサンプルの中の何十というものでも常に省略されています。DDDで子エンティティを作成する適切な方法

私はmy own answerから来ています。私はこの問題に関する私自身のビジョンに完全に満足していないので、私はこの問題について詳述する必要があると思った。

たとえば、会社名、モデル名、変更名(たとえば、日産ティーナ2012 - 日産の会社、Teanaモデル、2012年の変更)を表す単純なモデルを作成する必要があります。

私はこのようなルックスを作成したいモデルのスケッチ:

CarsCompany 
{ 
    Name 
    (child entities) Models 
} 

CarsModel 
{ 
    (parent entity) Company 
    Name 
    (child entities) Modifications 
} 


CarsModification 
{ 
    (parent entity) Model 
    Name 
} 

だから、今私は、コードを作成する必要があります。私は言語としてC#を使用し、ORMとしてNHibernateを使用します。これは重要であり、インターネット上の膨大なDDDサンプルには典型的には表示されません。

第1のアプローチ。

私は、ファクトリメソッドを使った典型的なオブジェクト作成を使って簡単なアプローチから始めます。

public class CarsCompany 
{ 
    public virtual string Name { get; protected set; } 
    public virtual IEnumerable<CarsModel> Models { get { return new ImmutableSet<CarsModel> (this._models); } } 


    private readonly ISet<CarsModel> _models = new HashedSet<CarsModel>(); 


    protected CarsCompany() 
    { 
    } 


    public static CarsCompany Create (string name) 
    { 
     if (string.IsNullOrEmpty (name)) 
      throw new ArgumentException ("Invalid name specified."); 

     return new CarsCompany 
     { 
      Name = name 
     }; 
    } 


    public void AddModel (CarsModel model) 
    { 
     if (model == null) 
      throw new ArgumentException ("Model is not specified."); 

     this._models.Add (model); 
    } 
} 


public class CarsModel 
{ 
    public virtual CarsCompany Company { get; protected set; } 
    public virtual string Name { get; protected set; } 
    public virtual IEnumerable<CarsModification> Modifications { get { return new ImmutableSet<CarsModification> (this._modifications); } } 


    private readonly ISet<CarsModification> _modifications = new HashedSet<CarsModification>(); 


    protected CarsModel() 
    { 
    } 


    public static CarsModel Create (CarsCompany company, string name) 
    { 
     if (company == null) 
      throw new ArgumentException ("Company is not specified."); 

     if (string.IsNullOrEmpty (name)) 
      throw new ArgumentException ("Invalid name specified."); 

     return new CarsModel 
     { 
      Company = company, 
      Name = name 
     }; 
    } 


    public void AddModification (CarsModification modification) 
    { 
     if (modification == null) 
      throw new ArgumentException ("Modification is not specified."); 

     this._modifications.Add (modification); 
    } 
} 


public class CarsModification 
{ 
    public virtual CarsModel Model { get; protected set; } 
    public virtual string Name { get; protected set; } 


    protected CarsModification() 
    { 
    } 


    public static CarsModification Create (CarsModel model, string name) 
    { 
     if (model == null) 
      throw new ArgumentException ("Model is not specified."); 

     if (string.IsNullOrEmpty (name)) 
      throw new ArgumentException ("Invalid name specified."); 

     return new CarsModification 
     { 
      Model = model, 
      Name = name 
     }; 
    } 
} 

このアプローチについての悪いことは、モデルの作成は、親モデルのコレクションに追加していないということです。

using (var tx = session.BeginTransaction()) 
{ 
    var company = CarsCompany.Create ("Nissan"); 

    var model = CarsModel.Create (company, "Tiana"); 
    company.AddModel (model); 
    // (model.Company == company) is true 
    // but (company.Models.Contains (model)) is false 

    var modification = CarsModification.Create (model, "2012"); 
    model.AddModification (modification); 
    // (modification.Model == model) is true 
    // but (model.Modifications.Contains (modification)) is false 

    session.Persist (company); 
    tx.Commit(); 
} 

トランザクションがコミットし、セッションがフラッシュされた後、ORMがします正しくすべてをデータベースに書き込んで、次回にその会社をロードするとモデルコレクションがモデルを正しく保持します。同じことが変更になります。この方法では、親エンティティがデータベースからリロードされるまで、矛盾した状態になります。立ち入り禁止。

第2のアプローチ。

今回は、言語固有のオプションを使用して、他のクラスの保護されたプロパティを設定する問題を解決します。つまり、セッターとコンストラクタの両方で "protected internal"修飾子を使用します。

public class CarsCompany 
{ 
    public virtual string Name { get; protected set; } 
    public virtual IEnumerable<CarsModel> Models { get { return new ImmutableSet<CarsModel> (this._models); } } 


    private readonly ISet<CarsModel> _models = new HashedSet<CarsModel>(); 


    protected CarsCompany() 
    { 
    } 


    public static CarsCompany Create (string name) 
    { 
     if (string.IsNullOrEmpty (name)) 
      throw new ArgumentException ("Invalid name specified."); 

     return new CarsCompany 
     { 
      Name = name 
     }; 
    } 


    public CarsModel AddModel (string name) 
    { 
     if (string.IsNullOrEmpty (name)) 
      throw new ArgumentException ("Invalid name specified."); 

     var model = new CarsModel 
     { 
      Company = this, 
      Name = name 
     }; 

     this._models.Add (model); 

     return model; 
    } 
} 


public class CarsModel 
{ 
    public virtual CarsCompany Company { get; protected internal set; } 
    public virtual string Name { get; protected internal set; } 
    public virtual IEnumerable<CarsModification> Modifications { get { return new ImmutableSet<CarsModification> (this._modifications); } } 


    private readonly ISet<CarsModification> _modifications = new HashedSet<CarsModification>(); 


    protected internal CarsModel() 
    { 
    } 


    public CarsModification AddModification (string name) 
    { 
     if (string.IsNullOrEmpty (name)) 
      throw new ArgumentException ("Invalid name specified."); 

     var modification = new CarsModification 
     { 
      Model = this, 
      Name = name 
     }; 

     this._modifications.Add (modification); 

     return modification; 
    } 
} 


public class CarsModification 
{ 
    public virtual CarsModel Model { get; protected internal set; } 
    public virtual string Name { get; protected internal set; } 


    protected internal CarsModification() 
    { 
    } 
} 

... 

using (var tx = session.BeginTransaction()) 
{ 
    var company = CarsCompany.Create ("Nissan"); 
    var model = company.AddModel ("Tiana"); 
    var modification = model.AddModification ("2011"); 

    session.Persist (company); 
    tx.Commit(); 
} 

今回、各エンティティの作成では、親エンティティと子エンティティの両方が一貫性のある状態になります。しかし、子エンティティ状態の検証は親エンティティに漏れました(AddModelおよびAddModificationメソッド)。私はDDDのどこにも専門家がいないので、大丈夫かどうかわからない。子エンティティのプロパティをプロパティで単純に設定できず、渡されたパラメータに基づいていくつかの状態を設定すると、パラメータ値をプロパティに割り当てるより複雑な作業が必要になることが、将来問題を引き起こす可能性があります。可能な限り、そのエンティティの内部にエンティティに関するロジックを集中させなければならないという印象を受けました。私にとって、このアプローチは、親オブジェクトを何らかの種類のエンティティ&ファクトリハイブリッドに変えます。

第3のアプローチ。

私たちは親子関係を維持する責任を逆転させます。

public class CarsCompany 
{ 
    public virtual string Name { get; protected set; } 
    public virtual IEnumerable<CarsModel> Models { get { return new ImmutableSet<CarsModel> (this._models); } } 


    private readonly ISet<CarsModel> _models = new HashedSet<CarsModel>(); 


    protected CarsCompany() 
    { 
    } 


    public static CarsCompany Create (string name) 
    { 
     if (string.IsNullOrEmpty (name)) 
      throw new ArgumentException ("Invalid name specified."); 

     return new CarsCompany 
     { 
      Name = name 
     }; 
    } 


    protected internal void AddModel (CarsModel model) 
    { 
     this._models.Add (model); 
    } 
} 


public class CarsModel 
{ 
    public virtual CarsCompany Company { get; protected set; } 
    public virtual string Name { get; protected set; } 
    public virtual IEnumerable<CarsModification> Modifications { get { return new ImmutableSet<CarsModification> (this._modifications); } } 


    private readonly ISet<CarsModification> _modifications = new HashedSet<CarsModification>(); 


    protected CarsModel() 
    { 
    } 


    public static CarsModel Create (CarsCompany company, string name) 
    { 
     if (company == null) 
      throw new ArgumentException ("Company is not specified."); 

     if (string.IsNullOrEmpty (name)) 
      throw new ArgumentException ("Invalid name specified."); 

     var model = new CarsModel 
     { 
      Company = company, 
      Name = name 
     }; 

     model.Company.AddModel (model); 

     return model; 
    } 


    protected internal void AddModification (CarsModification modification) 
    { 
     this._modifications.Add (modification); 
    } 
} 


public class CarsModification 
{ 
    public virtual CarsModel Model { get; protected set; } 
    public virtual string Name { get; protected set; } 


    protected CarsModification() 
    { 
    } 


    public static CarsModification Create (CarsModel model, string name) 
    { 
     if (model == null) 
      throw new ArgumentException ("Model is not specified."); 

     if (string.IsNullOrEmpty (name)) 
      throw new ArgumentException ("Invalid name specified."); 

     var modification = new CarsModification 
     { 
      Model = model, 
      Name = name 
     }; 

     modification.Model.AddModification (modification); 

     return modification; 
    } 
} 

... 

using (var tx = session.BeginTransaction()) 
{ 
    var company = CarsCompany.Create ("Nissan"); 
    var model = CarsModel.Create (company, "Tiana"); 
    var modification = CarsModification.Create (model, "2011"); 

    session.Persist (company); 
    tx.Commit(); 
} 

このアプローチは、対応するエンティティ内のすべての検証/作成ロジックを持って、それが良いか悪いかどうかは分かりませんが、ファクトリメソッドを持つオブジェクトを簡単に作成することにより、我々は暗黙のうちに親オブジェクトの子コレクションに追加。トランザクションのコミットとセッションの終了後、コードに「追加」コマンドを書き込んだことがない場合でも、データベースに3つの挿入があります。私はDDDの世界の外で私と私の広大な経験だとは思わないかもしれませんが、今はちょっと不自然です。

DDDで子エンティティを追加する最も正しい方法は何ですか?

+0

すべてのコード例を削除して、この全体的な質問を理解しにくくすることができますが、コミュニティに役立つとは思われません。私は、SOは複雑な問題ではないことを認識していませんでした。 –

+0

ここで問題(コードの有無にかかわらず)はすべて不適切です。私が言ったように、それは議論を求める。私が投稿したリンクを読んでください。私は別のものを提供することができます:[ディスカッションボードでもフォーラムでもない](http://meta.stackexchange.com/a/128550/172661)。私が前に言ったように、このサイトは議論なしで答えることができる**明確かつ簡潔な質問です**。あなたの質問全体が「最も正しい方法」を話し合い、「相手を聞く」ことを求めているので(ここでは適切ではない)、ここでは適切ではありません。 –

+3

私はかなり具体的な質問をしており、それに対する非常に特別な答えを待っています。私の例では、私はこの問題に取り組む研究の取り組みを示しています。私は議論を必要としません - 私はこのことを正しく行う方法を明確に回答する必要があります。あなたがこの質問に対する答えを知らないからといって、議論の本質を持っているわけではありません。 DDDの世界は多くのパターンを使用しています。私は、この特定の問題を解決するために私が適用すべきものを指摘するように頼んでいます。 –

答えて

1

私はここに許容可能な答えを持っている:https://groups.yahoo.com/neo/groups/domaindrivendesign/conversations/messages/23187

は基本的に、それは方法2と3の組み合わせだ - CarsCompanyにAddModel方法を入れて、それが検証されたnameパラメータを持つCarsModelの保護された内部コンストラクタを呼び出す作りますCarsModelのコンストラクタ内にあります。

+0

興味深いですが、親コレクションと子コレクションにオブジェクトを追加する場所はどこですか? –

+0

保護された内部コンストラクタによって子を作成する親.AddChildメソッド(コンストラクタ内で親を設定するために、子のパラメータとしてselfを渡します)を子コレクションに追加して返します。 –

+0

ありがとうございます。 Entity Frameworkと組み合わせてこのパターンを使用しましたか? –

0

ここでは非常に具体的で非常に正直な答えがあります:DDDの「最初のルール」を破った、つまりDBが存在しないため、すべてのアプローチが間違っています。

あなたが定義しているのは、ORM(nhibernate)のPERSISTENCEモデルです。ドメインオブジェクトを設計するには、最初にBounded Context、そのモデル、そのモデルのエンティティおよび値オブジェクト、およびAggregate Root(子およびビジネスルールを内部的に扱う)を特定する必要があります。

Nhibernateスキーマまたはdbスキーマは、ここには存在しません。純粋なC#コードとドメインの明確な理解のみが必要です。

+4

DDDの本のような素敵な音ですが、ここでの問題はドメインの設計ではありません。それを実装する際の問題。開発時には、非常に特定の問題を解決するための非常に特殊なパターンがあります。ドメインを作業コードに鍛錬するという問題です。そして私はそれを実装する正しい方法が何であるかを指摘するように求めています。私は純粋な架空の言葉でそれを書くことができ、それはその架空の世界で働くことができます(私はそれに問題がない)。 –

+2

難しいのはドメインを設計することです。実際にBCとAggreagatesを確立することは最も難しいことです。あなたがそれらを(pseduoのコードで)知っていれば、それはC#で書くだけの形式です。これは理論ではなく、DDDの最も難しい部分は、DOMAINを理解し、どれがどれであるかを明確に区別することです。また、銀色の弾や、どんなパターンを適用する必要もありません。 DDDは概念とガイドラインに関するものです。 Nhibernateでいくつかのリレーションシップを実装する必要があります。それはDDDではありません。 – MikeSW

+1

DOMAIN EXPERTを使用すると、問題とその影響を理解するのに役立ちます。私や他の人々はドメイン専門家ではありません。専門家に相談して、解決すべき問題が何であるかを明確に理解する必要があります。正直なところ、BCを明確に知らなくても、EntityやAggregateを設計し実装することはできません。 – MikeSW

0

DDDで子エンティティを追加する最も正しい方法は何ですか?

第3のアプローチはTight Couplingと呼ばれます。 Company,CarおよびModificationはお互いについてほとんどすべてを知っています。

第2のアプローチは、DDDにおいて広く提案されている。ドメインオブジェクトは、ネストされたドメインオブジェクトを作成して内部に登録する役割を担います。

最初のアプローチは古典的なOOPスタイルです。オブジェクトの作成は、オブジェクトをあるコレクションに追加することとは別のものです。このようにして、コード消費者は、コンクリートクラス(例えばCar)のオブジェクトを任意の派生クラス(例えばTrailerCar)のオブジェクトで置き換えることができる。

// var model = CarsModel.Create (company, "Tiana"); 

var model = TrailerCarsModel.Create (
    company, "Tiana", SimpleTrailer.Create(company)); 

company.AddModel (model); 

第2または第3のアプローチでは、このビジネスロジックの変更を採用してください。

0

興味深い。 DDDとリポジトリ/ ORMのナビゲーションプロパティ。回答は、あなたが1つの集約か2つを扱っているかによって異なります。 CarsModelはCarsCompany集合体の一部か、それとも独自の集合体であるべきですか?

1つは、問題を解決することです。 MikeSWはこれを暗示した。 CarsCompanyとCarsModelが同じ集合体の一部である必要がない場合は、アイデンティティによって互いに参照するだけで、ナビゲーションプロパティはドメイン内に表示されません。

アプローチ2は、集計のフェッチと同じ方法で関係を追加することです。アプリケーションサービスがリポジトリからメソッドを呼び出すようにします。これは、ORM固有の問題が解決される正しい場所です。このような方法では、関係の両端を埋めることができます。

関連する問題