C# 如何在DDD中建模聚合并持久化到数据库

C# 如何在DDD中建模聚合并持久化到数据库,c#,domain-driven-design,aggregate,C#,Domain Driven Design,Aggregate,我只是想摆脱典型的存储库/服务/表示的N层体系结构的舒适区,开始研究聚合DDD,我不得不承认我有点困惑,希望有人能澄清以下示例: 如果我有一个名为News、NewsImage和Customer的实体,它们都是EF持久化对象,如下所示: public class Customer { public virtual int Id { get; set; } public virtual string Name { get; set; } } public class NewsIma

我只是想摆脱典型的存储库/服务/表示的N层体系结构的舒适区,开始研究聚合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; }
}
我的问题如下,我希望得到任何澄清,因为我确信我误解了这里的基本原则:

  • 只有聚合对象应该公开给表示层(或任何使用层)
  • 我如何处理将聚合对象转换/持久化到数据库,我可以使用映射,这很好,但是我如何知道我是否正在创建对象或更新(如果设置了Id,它是否是瞬态的?)。如何知道是否添加了新图像以及更新或删除哪些图像?我想我遇到的问题是,我调用create将新闻聚合传递到存储库并创建它,然后我可以从通过实体EF填充的域中获取聚合,然后添加一个图像,当我将新闻聚合传递回时,我如何知道为了创建/更新数据发生了什么变化?
  • 客户应该去哪里,应该作为AddCustomer方法出现在news aggregate对象上,应该有一个CustomerAggregate具有AddNews方法,并且使用这两个选项如何持久

  • 非常感谢您提供的见解,我一直在阅读和查看示例项目,这些项目演示了该概念,但似乎没有充分解释实现这一点的最佳方法。

    首先:DDD不建议您使用任何特定的体系结构。我在DDD中使用了许多不同的体系结构,您应该使用对任务有好处的体系结构。显然,如果以数据驱动的方式思考,DDD将遇到许多问题

    DDD是一种设计用于处理复杂业务规则的方法。如果您的应用程序价值在于技术资产(如在云中、公开web服务或一些不错的html5/mobile UI),那么您不应该使用它,而是在于它所处理的业务的复杂性。
    对于简单的业务规则,不应使用DDD。拇指法则是:如果你不需要领域专家来了解业务,你根本不需要DDD

    然后,为了正确理解聚合,您应该阅读弗农的。 这篇文章解释了聚合的存在是为了确保业务不变性。 您不应仅使用聚合来优化数据库访问。

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

    此外,聚合不是单个类,而是围绕作为实体的聚合根聚集的一组类。您不一定需要单独的类来表示聚合,它可以是根实体

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

    2.1)这取决于
    Customer
    本身是否为聚合。如果是,则
    新闻
    应仅通过ID引用
    客户
    。此外,新闻实体可能需要客户,在这种情况下,客户ID应该传递给新闻实体的构造函数。如果不是必需的,则存在将客户与新闻实体关联的行为。从域的角度考虑这一点——客户与新闻实体的联系是什么意思?试着从技术性的、粗俗的思维方式(如
    AddCustomer
    )中摆脱出来,更多地考虑周围的业务意图

    正如Giacomo Tesio所指出的,DDD在业务逻辑较为复杂的领域中显示了其价值。如果您的所有行为都可以映射到CRUD,则将其保留为CRUD。否则,在您的领域中寻找行为,而不是关注数据。您的实体和值对象应该尽可能地公开行为和隐藏状态。请阅读并重新阅读引用的文章:

    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?
        }
    }