私は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で子エンティティを追加する最も正しい方法は何ですか?
すべてのコード例を削除して、この全体的な質問を理解しにくくすることができますが、コミュニティに役立つとは思われません。私は、SOは複雑な問題ではないことを認識していませんでした。 –
ここで問題(コードの有無にかかわらず)はすべて不適切です。私が言ったように、それは議論を求める。私が投稿したリンクを読んでください。私は別のものを提供することができます:[ディスカッションボードでもフォーラムでもない](http://meta.stackexchange.com/a/128550/172661)。私が前に言ったように、このサイトは議論なしで答えることができる**明確かつ簡潔な質問です**。あなたの質問全体が「最も正しい方法」を話し合い、「相手を聞く」ことを求めているので(ここでは適切ではない)、ここでは適切ではありません。 –
私はかなり具体的な質問をしており、それに対する非常に特別な答えを待っています。私の例では、私はこの問題に取り組む研究の取り組みを示しています。私は議論を必要としません - 私はこのことを正しく行う方法を明確に回答する必要があります。あなたがこの質問に対する答えを知らないからといって、議論の本質を持っているわけではありません。 DDDの世界は多くのパターンを使用しています。私は、この特定の問題を解決するために私が適用すべきものを指摘するように頼んでいます。 –