DDD聚合根持久性

时间:2014-06-20 18:30:56

标签: c# domain-driven-design aggregateroot

我有一个聚合根的类,代表一个Person。一个人必须拥有一个Title(Mr,Mrs,Ms等),这是Person对象的属性。创建人员时,用户必须从下拉列表中选择一个标题,其内容通过另一个页面进行管理。

还可以选择通过只读页面查看此信息,因此通过使用下面的模型,我可以使用ID和名称"手持"并且不必离开并根据标题ID检索标题的ValueName,如果我将TitleId添加为Person而不是Title的属性,我将不得不这样做。

保存人物对象时,标题的ID将作为人员的一部分保留,并存储在人员数据库表中(不会触及包含标题的数据库表)

当填充人员时,存储过程根据标题ID将人员加入到标题中,并返回用于填充人员标题类别的信息。

public class Person : IAggregateRoot, IPerson 
{
   public string Forename { get;set; }
   public string Surname{ get;set; }
   public ITitle Title { get;set; }
}

public class Title : IAggregateRoot , ITitle
{
    public Guid Id {get;set;}
    public string ValueName {get;set;}
}

我的问题是:从DDD的角度来看,可以使用这个类结构并将聚合根对象嵌套在另一个聚合根对象中,因为它是一个"固定列表"或者"查找值"还需要由管理员单独维护吗?

2 个答案:

答案 0 :(得分:1)

根据定义,聚合根是聚合的“ROOT”,因此对您的问题的简答题是否定的,聚合根不能嵌套在另一个聚合根中。

请记住,聚合根完全取决于上下文。因此,在管理“人物”的上下文中,标题只是一个实体或价值对象。在管理“标题”的上下文中,标题可以是聚合根。如果是这种情况,则应该有两个单独的“标题”类,每个类对应一个上下文。这是一种变更管理策略,因此在一个上下文中进行的更改不会影响其他上下文。

这是一个可能有助于阐明这个问题的例子。请原谅这些名称,因为需要更多关于您的域名的知识来制作更有意义的名称,所以我将为此示例的目的做出一些假设:

雇用某人的背景

public class Person : IAggregateRoot, IPerson 
{
   public string Forename { get;set; }
   public string Surname{ get;set; }
   public Title Title { get;set; }
   public DateTime? HireDate { get; set; }
   public void Hire(Title granted, IPersonRepository repository)
   {
       Title = granted;  //Grant the new hire this title that we have at our company
       HireDate = DateTime.Now;
       repository.Save(this);
   }
}

public struct Title /* I like to make my value objects structs */
{
    public Title (Guid id, string value)
    {
        Id = id;
        Value = value;
    }
    public Guid Id { get; private set; }
    public string FullTitle{ get; private set; }  /* This would be the prefix + value + level when loaded from the repository because, in this context, we don't have any need for that level of separation. */
}

创建具有不同级别的新标题的上下文

public class Title : IAggregateRoot, ITitle
{
    public string Prefix { get; set; }
    public string Value { get; set; }
    public int Level { get; private set; }
    public void Create(int levelsAvailable, ITitleRepository repository)
    {
        for (int i = 1; i <= levelsAvailable; i++)
        {
            Title title = new Title(Prefix, Value) { Level = i };
            repository.Save(title);
        }
    }
}

public class Person : IEntityObject, IPerson 
{
   public ITitle Title { get;set; }
   public string Name { get; set; }
}

为了阐明变更管理策略的原因,请考虑业务是否回来并说每个新员工都要获得90天的试用期权,并且试用期权被定义为1级职称。使用上面的策略,只需要更改一个上下文,这可以消除其他上下文中任何可能的未知故障。

希望这有帮助!

答案 1 :(得分:0)

不,嵌套聚合根也不行。聚合可以包含多个实体,或引用(通过id)到另一个聚合,但它不能封装聚合。

我也会说&#34; Title&#34;不是实体的合适候选人(汇总或其他)。它应该被视为一个&#34;值对象&#34;,实际上 - 它应该只是一个字符串。您可能有一个单独的标题查找表,但这并不意味着您应该从Person对象引用该表。

将标题查找表的维护过程与人员的标题分配分开。选择标题后,将字符串复制到person对象。非规范化是使用值对象的方式。