如何在DDD中建模聚合并保持数据库

时间:2013-03-31 10:51:31

标签: c# domain-driven-design aggregate

我只是想摆脱典型的N-Tier存储库/服务/演示架构的舒适区域,并开始使用Aggregates查看DDD,我不得不承认我有点困惑,希望有人可以澄清以下示例:

如果我有一个名为News,NewsImage和Customer的实体,它们都是EF可持续存在的对象,如下所示:

public class Customer
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}

public class NewsImage
{
    public virtual int Id { get; set; }
    public virtual byte[] Data { get; set; }
    public virtual News News { get; set; }
}

public class News
{ 
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual ICollection<NewsImage> NewsImages { get; set; }
    public virtual Customer Customer { get; set; }
}

据我所知,这些可能是我们用来将域对象持久保存到数据库的对象,但如果我们使用域模型中的聚合,我们就可以这样:

public class NewsAggregate
{
    public int Id { get; set; }
    public string Name { get; set }

    public void AddImageToNews(byte[] imageData)
    {
         // Hide NewsImage or that object and add the byte[] data here?
    }
}

我的问题随之而来,我将不胜感激,因为我确信我在这里误解了基本原则:

  1. 只有聚合对象应该暴露给表示层(或任何消费层)。
  2. 如何处理将聚合对象转换/持久化到数据库,我可以使用很好的映射但是如何知道我是创建对象还是更新(如果设置了Id,它是否是瞬态的)不?)。如何知道是否添加了新图像以及更新或删除了哪些图像?我认为我遇到的问题是我调用create将新闻聚合传递给Repository并创建它,然后我可以从通过EF实体填充的域中获取聚合,然后在我传递新闻聚合时添加图像回到如何知道为了创建/更新数据而改变了什么?
    1. 客户应该去哪里,如果它作为AddCustomer方法在新闻聚合对象上,是否应该有一个具有AddNews方法的CustomerAggregate以及这两个选项如何保持?
  3. 非常感谢任何见解,我一直在阅读并查看展示概念的示例项目,但似乎并没有完全解释实现这一目标的最佳方法。

2 个答案:

答案 0 :(得分:2)

首先:DDD并不建议您使用任何特定的架构。我已经使用了许多不同的DDD架构,你应该使用对任务有益的东西。显然,如果你以数据驱动的方式思考,你会遇到许多DDD问题。

DDD是一种旨在应对复杂业务规则的方法。如果您的应用程序价值属于技术资产(如在云中,暴露Web服务或一些不错的html5 /移动UI),那么您不应该使用它,而是处理它处理的业务的复杂性。 您不应该将DDD用于简单的业务规则。经验法则是:如果您不需要域专家来理解业务,则根本不需要DDD。

然后,为了正确理解聚合,你应该阅读Vernon的essay on the topic。 该文章解释了聚合存在以确保业务不变量。 您永远不应该使用聚合只是来优化数据库访问。

答案 1 :(得分:2)

1)这取决于容量。有一条规则规定聚合只能直接引用其他聚合 - 而不是其他聚合中包含的实体或值对象。这是为了将聚合强制实施为一致性边界 - 它们完全封装了它们“聚合”的内容。每个聚合应该有一个存储库。表示层和任何外层可能需要以两种常规容量引用聚合 - 用于显示目的或用于行为目的。聚合不应过多关注它的显示方式,因为查询可以使用更适合任务的不同模型来实现 - read-model。相反,聚合应该关注行为。是的,在表示层希望在聚合上执行行为的情况下,它应该通过其标识引用聚合。更好的是,创建一个application service来封装域层,并将行为公开为一个简单的外观。

此外,聚合不是单个类,而是通常围绕聚合根(一个实体)聚集的一组类。您不一定需要一个单独的类来表示聚合,它可能只是根实体。

2)对于持久性,似乎您正在使用EF,它应该为您处理所有更改跟踪。它应该跟踪哪些对象是持久的或哪些是瞬态的。像NHibernate这样的ORM也可以这样做。

2.1)这取决于Customer本身是否为聚合。如果是,则News应仅按ID引用Customer。此外,新闻实体可能需要客户,在这种情况下,客户ID应该传递给新闻实体的构造者。如果不需要,则存在将客户与新闻实体相关联的行为。从域的角度考虑这一点 - 将客户与新闻实体相关联的含义是什么?尽量避免以AddCustomer这样的技术CRUD方式思考,并考虑周围的商业意图。

正如 Giacomo Tesio 所指出的,DDD在业务逻辑中具有一定复杂性的域中显示其价值。如果您的所有行为都可以映射到CRUD,那么请将其保留为CRUD。否则,请在您的域中查找行为,而不是关注数据。您的实体和值对象应该尽可能地暴露行为并隐藏状态。请阅读并重新阅读引用的文章:Effetive Aggregate Design by Vaughn Vernon