【问题标题】:Domain Driven Design: When to make an Aggregate Root?领域驱动设计:何时创建聚合根?
【发布时间】:2009-10-03 20:00:37
【问题描述】:

我第一次尝试使用 ASP.NET MVC 项目实现 DDD,但遇到了一些问题。

我有 2 个相关实体,一个公司和一个供应商。我最初的想法是 Company 是一个聚合根,而 Supplier 是 Company 的一个价值对象。所以我有一个公司的存储库,而供应商没有。

但是当我开始构建我的应用程序时,我最终需要为供应商单独列出、创建和更新表单。这个列表很简单,我可以调用 Company.Suppliers,而创建很糟糕,我可以做 Company.Suppliers.Add(supplier),但更新让我头疼。由于我只需要一个实体,而且我不能在表单之间准确地将其粘贴到内存中,因此我最终需要重新获取公司和所有供应商,并找到我需要绑定到它的那个,然后再次对其进行修改并保留它回到数据库。

如果我有供应商的存储库,我真的只需要做一个 GetOne。我可以通过将 GetOneSupplier 添加到我的 Company 或 CompanyRepository 来添加一些解决方法,但这似乎很垃圾。

所以,我真的想知道它是否实际上是一个值对象,而不是一个完整的域实体本身。

tldr;

需要单独的列表/创建/更新视图/页面是否表明实体应该是它自己的根?

【问题讨论】:

    标签: asp.net-mvc domain-driven-design bdd


    【解决方案1】:

    根据您的术语,我假设您正在根据 Eric Evans 的书执行 DDD。听起来您在最初的建模过程中已经发现了一个问题,并且您是正确的。

    您提到您将供应商视为Value Object...我认为不是。 Value Object 主要由其属性标识。例如,日期“2009 年 9 月 30 日”是一个值对象。为什么?因为具有不同月/日/年组合的所有日期实例都是不同的日期。具有相同月/日/年组合的所有日期实例都被视为相同。我们永远不会争论将我的“2009 年 9 月 30 日”换成你的,因为它们是一样的 :-)

    另一方面,Entity 主要由其“ID”标识。例如,银行账户有 ID - 它们都有帐号。如果一家银行有两个账户,每个账户有 500 美元,如果他们的帐号不同,那么它们也不同。它们的属性(在这个例子中,它们的平衡)不能识别它们或暗示相等。我敢打赌,即使银行账户的余额相同,我们也会争论交换银行账户:-)

    因此,在您的示例中,我会将供应商视为Entity,因为我假设每个供应商主要由其 ID 而不是其属性来标识。我自己的公司与世界上另外两家公司同名 - 但我们并非都可以互换。

    我认为您的建议是,如果您需要视图来对对象进行 CRUD,那么它是 Entity 作为经验法则可能是正确的,但您应该更多地关注使一个对象与其他对象不同的原因:属性或 ID。

    现在就Aggregate Roots 而言,您希望专注于对象的生命周期和访问控制。考虑一下我的博客有很多帖子,每个帖子都有很多 cmets - Aggregate Root(s) 在哪里?让我们从 cmets 开始。在没有帖子的情况下发表评论有意义吗?你会创建一个评论,然后去找一个帖子并将其附加到它上面吗?如果你删除一个帖子,你会保留它的 cmets 吗?我建议一个帖子是Aggregate Root,带有一个“叶子” - cmets。现在考虑博客本身——它与帖子的关系类似于帖子和 cmets 之间的关系。在我看来,Aggregate Root 也有一个“叶子” - 帖子。

    因此,在您的示例中,公司和供应商之间是否存在牢固的关系,如果您删除一家公司(我知道......您可能只有一个公司实例),您也会删除其供应商?如果你删除“星巴克”(美国的一家咖啡公司),它所有的咖啡豆供应商都将不复存在吗?这一切都取决于您的域和应用程序,但我建议您的 Entities 很可能都不是 Aggregate Roots,或者考虑它们的更好方法是它们都是没有“叶子”的聚合根(没有总计的)。换句话说,公司不控制对供应商的访问或控制供应商的生命周期。它只是与供应商建立一对多的关系(或者可能是多对多)。

    这将我们带到RepositoriesRepository 用于存储和检索Aggregate Roots。你有两个(从技术上讲,它们没有聚合任何东西,但比说“存储库存储聚合根或不是聚合中的叶子的实体”更容易),因此你需要两个Repositories。一份给公司,一份给供应商。

    我希望这会有所帮助。也许 Eric Evans 潜伏在这里,会告诉我我在哪里偏离了他的范式。

    【讨论】:

    • 很棒的答案!你帮助验证了我的想法并解释了很多。太感谢了!我同意,如果存在供应商,则应该共享对供应商的引用,但目前它将是用户输入的数据,无法验证 2 个供应商是否相同。随着系统的发展,这可能会在未来发生变化,但现在就是这样。再次感谢!
    • 我同意这是一个很好的解释,比我通常在 DDD 邮件列表中找到的更有帮助!
    • 它来自一本书 (Domain Driven Design)!
    【解决方案2】:

    对我来说听起来很简单 - 供应商应该有自己的存储库。如果一个实体在模型中独立存在的任何逻辑可能性,那么它应该是一个根实体,否则无论如何你最终都会重构,这是多余的工作。

    根实体总是比值对象更灵活,尽管预先进行了额外的实现工作。我发现随着模型的发展,模型中的值对象会随着时间的推移而变得越来越少,而保留为值对象的实体通常是您可以从第一天开始就以这种方式进行逻辑约束的实体。

    如果公司共享供应商,那么将供应商作为根实体也会消除数据冗余,因为您不会复制每个公司的供应商定义而是共享参考,而且公司和供应商之间的关联也可以是双向的,这可能会产生更多的好处。

    【讨论】:

      猜你喜欢
      • 2010-11-01
      • 1970-01-01
      • 2010-11-30
      • 2011-04-26
      • 1970-01-01
      • 1970-01-01
      • 2013-09-16
      相关资源
      最近更新 更多