Mic*_*tov 10 c# nhibernate domain-driven-design parent-child
我是DDD世界的新手,在阅读了几本关于它的书之后(其中包括Evans DDD)我无法在互联网上找到我的问题的答案:用DDD创建子实体的正确方法是什么?你看,互联网上的很多信息都在一些简单的层面上运作.但是细节上的恶魔并没有为了简单起见而在数十个DDD样本中省略它们.
我在stackoverflow 上来自我自己在similair问题上的回答.我对这个问题的看法并不完全满意,所以我想我需要详细说明这个问题.
例如,我需要创建代表汽车命名的简单模型:公司,模型和修改(例如,日产天籁2012 - 将是"日产"公司,"天籁"模型和"2012"修改).
我想要创建的模型的草图如下所示:
CarsCompany
{
Name
(child entities) Models
}
CarsModel
{
(parent entity) Company
Name
(child entities) Modifications
}
CarsModification
{
(parent entity) Model
Name
}
Run Code Online (Sandbox Code Playgroud)
所以,现在我需要创建代码.我将使用C#作为语言,将NHibernate用作ORM.这很重要,通常在互联网上的大量DDD样本中没有显示.
第一种方法.
我将从通过工厂方法创建典型对象的简单方法开始.
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
};
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法的坏处是模型的创建不会将其添加到父模型集合:
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 ();
}
Run Code Online (Sandbox Code Playgroud)
提交事务并刷新会话后,ORM将正确地将所有内容写入数据库,下次我们加载该公司时,它的模型集合将正确保存我们的模型.修改也是如此.因此,这种方法使我们的父实体处于不一致状态,直到它从数据库重新加载.不行.
第二种方法.
这次我们将使用特定于语言的选项来解决设置其他类的受保护属性的问题 - 即我们将在setter和构造函数上使用"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 ();
}
Run Code Online (Sandbox Code Playgroud)
这次每个实体创建都使父实体和子实体保持一致状态.但是,子实体状态的验证泄漏到父实体(AddModel和AddModification方法)中.由于我不熟悉DDD,我不确定它是否合适.如果不能通过属性简单地设置子实体属性并且基于传递的参数设置某些状态将需要更复杂的工作来为属性分配参数值,那么将来可能会产生更多问题.我的印象是,我们应该在任何可能的地方集中关于实体内部的逻辑.对我来说,这种方法将父对象转变为某种实体和工厂混合.
第三种方法.
好的,我们将颠倒维持亲子关系的责任.
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 ();
}
Run Code Online (Sandbox Code Playgroud)
这种方法在相应的实体中获得了所有验证/创建逻辑,我不知道它是好还是坏,但是通过使用工厂方法简单地创建对象,我们隐式地将它添加到父对象子集合中.在事务提交和会话刷新之后,即使我从未在代码中编写一些"添加"命令,也会有3个插入数据库.我不知道也许只是我和我在DDD世界之外的丰富经验,但现在感觉有点不自然.
那么,使用DDD添加子实体的最正确方法是什么?
我在这里得到了可接受的答案:https ://groups.yahoo.com/neo/groups/domaindrivendesign/conversations/messages/23187
基本上,它是方法 2 和 3 的组合 - 将 AddModel 方法放入 CarsCompany 中,并使其使用名称参数调用 CarsModel 的受保护内部构造函数,该参数在 CarsModel 的构造函数内进行验证。
| 归档时间: |
|
| 查看次数: |
5286 次 |
| 最近记录: |