问题标签 [aggregateroot]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
1 回答
1430 浏览

design-patterns - 如果实体成为聚合的根,聚合根是使用根实体的现有 ID 还是 AR 创建自己的 ID?

在领域驱动设计 (DDD) 中,实体始终具有自己的唯一身份。

在我阅读 DDD 时,我看到了似乎在实体和聚合根之间混合了“唯一身份”概念的陈述和示例。根据示例,它们可以暗示:

  • 我只需要以下接口之一。

或者

  • 我两个都需要。

我想知道就“Eric Evans type DDD”而言,哪种方法是“正确的”。

例如,假设您的实体实现了此接口并在调用此方法时返回一个 GUID:

您是否也需要下面的那个?

Aggregate Root 是否需要实现与上述类似的接口,以便它可以表示其自己的唯一 ID (IdThatIsUniqueForThisAggregateRootObject),该 ID 与其根实体的 ID (IdThatIsUniqueForThisEntityObject) 不同且不同?

或者聚合根应该只使用根实体的 (IdThatIsUniqueForThisEntityObject) 来表示聚合根的唯一 ID?

0 投票
1 回答
628 浏览

nhibernate - 使用 DDD 构建的公共网站如何避免拥有一个主要的聚合根

我正在尝试使用域驱动设计,同时创建一个可公开访问的网站。我遇到的一个问题是试图弄清楚我的模型的聚合根应该是什么。我非常清楚哪些对象是实体对象,哪些对象是值对象。

我的站点与大多数公共站点一样,不允许每个用户查看存储在站点中的每条信息。相反,他们只能看到自己拥有的信息。对于我的站点,用户将创建“项目”,他们也可以与其他用户共享。然而,用户仍然只能看到他们创建或受邀加入的项目中的信息。我的模型中的所有其他对象都存在于一个项目中,如果一个项目被删除,它包含的所有对象也应该被删除。

那么这是否意味着我应该拥有一个主要的“Project”聚合根类型和一个“ProjectRepository”存储库?每次请求我网站上的任何页面时,加载整个项目对我来说似乎效率低下。实际上,这不是什么大问题,因为我使用的是 NHibernate,它只会延迟加载项目中请求的项目。然而,让网站的效率如此依赖于使用延迟加载的 ORM 似乎是糟糕的设计。


这是一个更新,希望能让我的问题更清楚。

首先,我试图了解我的项目类型是否应该是我的模型的聚合根。项目可以单独存在,而报告必须存在于项目中。如果删除项目,则应删除相应的报告。这是否意味着 Project 可以或应该是聚合根?这个我不是很清楚。

如果 Project 是聚合根,那么 Report 应该不正确?据我了解,根不应嵌套在 DDD 中。此外,只允许从存储库中检索聚合根。因此,如果 Report 不是聚合根目录,那么我不应该有 ReportsRepository,而应该只通过从 ProjectsRepository 检索到的项目访问报告。因此,即使页面只需要来自单个报表的数据,它也需要从 ProjectRepository 加载整个项目才能获取报表。

此外,如果 Project 是包含 Reports 的聚合根,则也可以设置从 ProjectRepository 中删除 Project 以删除它包含的 Reports。但是,如果 Project 和 Report 都是聚合根,那么在删除 Project 时不允许 ProjectRepository 删除 Reports 会打破聚合之间的界限吗?聚合根及其相应的存储库不是应该相互独立吗?

0 投票
2 回答
229 浏览

orm - 绕过聚合根以编辑单个模型

这是对另一个问题的跟进:绕过聚合根

这个问题的作者询问在他的示例中是否可以接受绕过聚合根。我有同样的问题,但针对不同的用例。

我们的 Web 应用程序有一个后台,我们可以在其中编辑属于聚合根的所有项目:

  • 产品(聚合根
  • 选项
  • 选项项目

等等

因为一个产品不是在一个批次中创建的,它的所有选项,我们在一个单独的屏幕中一个一个地编辑聚合根中的每个实体。由于我们处于无状态系统中,因此我们需要在 URL 参数中唯一标识我们想要处理的实体。这意味着,例如,我们将根据其获取和编辑选项id,而不是从根目录浏览。

恕我直言,为每个人都有一个存储库更有意义,只需执行以下操作:

而不是类似的东西:

这是否打破了领域驱动设计中聚合根的概念?在这种情况下,我将非常感谢学习正确的方法。

0 投票
2 回答
5079 浏览

domain-driven-design - 我应该如何强制聚合根之间的关系和约束?

关于 DDD 模型中两个聚合根之间的引用之间的关系,我有几个问题。请参阅下图所示的典型客户/订单模型。

在此处输入图像描述

首先,聚合的实际对象实现之间的引用是否应该总是通过 ID 值而不是对象引用来完成?例如,如果我想要有关订单客户的详细信息,我需要获取 CustomerId 并将其传递给 ICustomerRepository 以获取客户,而不是设置订单对象以直接返回客户是否正确?我很困惑,因为直接返回客户似乎会使针对模型编写代码更容易,并且如果我使用像 NHibernate 这样的 ORM,设置起来并不难。然而,我相当肯定这将违反聚合根/存储库之间的界限。

其次,应该在哪里以及如何对两个聚合根执行级联删除关系?例如,假设我希望在删除客户时删除所有关联的订单。ICustomerRepository.DeleteCustomer() 方法不应该引用 IOrderRepostiory 吗?这似乎会打破聚合/存储库之间的界限?我是否应该有一个 CustomerManagment 服务来处理删除客户及其关联的订单,这将引用 IOrderRepository 和 ICustomerRepository?在这种情况下,我如何确定人们知道使用服务而不是存储库来删除客户。这仅仅是教育他们如何正确使用模型吗?

0 投票
2 回答
751 浏览

domain-driven-design - 域驱动设计中的聚合根复杂度

聚合的复杂性在哪里划清界限?澄清一下,如果我的聚合有一个 ObjectA 列表,其中有一个 ObjectB 列表,其中有一个 ObjectC 列表,我的聚合是否应该负责检索 ObjectC?或者我应该考虑创建另一个聚合以将这种复杂性降低到层次结构中的几个级别?

0 投票
1 回答
1062 浏览

.net - 存储库模式 - 聚合根

我试图弄清楚聚合根在我的实体框架数据模型中的位置,所以我知道我需要创建哪些存储库。

如果我用关系数据库术语谈一谈,我有一个 ExceptionGroup 对象和一个 Exception 对象(不是 system.exception!)。Exception 属于 ExceptionGroup,没有 ExceptionGroup 就不能存在。

我应该为每个对象都有一个存储库还是一个包含两者方法的存储库?如果我有一个单一的存储库,方法如下......

0 投票
1 回答
21722 浏览

c# - 具有实体框架 4.1 和父/子关系的存储库模式

我仍然对存储库模式有些困惑。我想使用此模式的主要原因是避免从域调用 EF 4.1 特定的数据访问操作。我宁愿从 IRepository 接口调用通用 CRUD 操作。这将使测试更容易,如果将来我必须更改数据访问框架,我将能够这样做而无需重构大量代码。

这是我的情况的一个例子:

我在数据库中有 3 个表:GroupPersonGroupPersonMap. GroupPersonMap是一个链接表,只包含主键GroupPerson主键。我用 VS 2010 设计器创建了 3 个表的 EF 模型。EF 足够聪明,可以假设GroupPersonMap是一个链接表,因此它不会在设计器中显示它。我想使用我现有的域对象而不是 EF 生成的类,所以我关闭了模型的代码生成。

我现有的匹配EF模型的类如下:

我有一个像这样的通用存储库接口:

和一个通用的 EF 存储库:

现在假设我只想将现有的映射Group到现有的Person. 我将不得不执行以下操作:

但这不起作用,因为 EF 认为Person是新的,并且会尝试重新INSERT进入Person数据库,这将导致主键约束错误。我必须使用DbSet'Attach方法告诉 EFPerson数据库中已经存在,所以只需在表之间Group和表中创建一个映射。PersonGroupPersonMap

因此,为了附加Person到上下文,我现在必须Attach向我的 IRepository 添加一个方法:

修复主键约束错误:

固定的。现在我必须处理另一个问题,Group即每次创建组/人员映射时都会在数据库中进行更新。这是因为在我的EFRepository.Update()方法中,实体状态被显式设置为Modified'. I must set the Group's state toUnchanged so theGroup 表不会被修改。

为了解决这个问题,我必须向我的 IRepository 添加某种Update重载,它不会更新根实体,或者Group,在这种情况下:

Update 方法的 EF4 实现如下所示:

我的问题是:我以正确的方式接近这个吗?当我开始使用 EF 和存储库模式时,我的存储库开始看起来以 EF 为中心。感谢您阅读这篇长文

0 投票
1 回答
3006 浏览

orm - 更新聚合内的实体

我正在阅读关于 SO 的类似问题:How update an entity inside Aggregate,但我仍然不确定用户界面应如何与聚合内的实体交互。

假设我有一个User, 和一堆Addresses。User 是聚合根,而 Address 只存在于聚合中。

在 Web 界面上,用户可以编辑他的地址。基本上,会发生什么:

  • 用户在其 Web 界面上看到地址列表
  • 他点击一个地址,然后被重定向到这个页面:edit-address?user=1&address=2
  • 在这个页面上,他得到一个表格,他可以在其中修改这个地址。

我决定绕过聚合根,这很简单:

  • 我们将直接Address加载Id
  • 我们会更新它,然后保存它

因为我们想用 DDD 的方式来做,所以我们有不同的解决方案:

  1. 我们要么要求用户通过 Id 获取此地址

    address = user.getAddress(id);
    address.setPostCode("12345");
    address.setCity("New York");
    em.persist(user);

    这种方法的问题是,IMO,聚合根仍然无法更好地控制地址的处理方式。它只是返回对它的引用,因此这与绕过聚合没有太大区别。

  2. 或者我们告诉聚合更新现有地址

    user.updateAddress(id, "12345", "New York");
    em.persist(user);

    现在聚合可以控制对该地址的处理,并且可以采取任何与更新地址相关的必要操作。

  3. 或者我们将地址视为一个值对象,我们不更新我们的Address,而是删除它并重新创建它

    user.removeAddress(id);
    address = new Address();
    address.setPostCode("12345");
    address.setCity("New York");
    user.addAddress(address);
    em.persist(user);

    最后一个解决方案看起来很优雅,但意味着地址不能是实体。那么,如果它需要被视为一个实体,例如因为聚合中的另一个业务对象具有对它的引用,该怎么办?

我很确定我在这里遗漏了一些东西来正确理解聚合概念以及它是如何在现实生活中的例子中使用的,所以请不要犹豫,提出你的意见!

0 投票
2 回答
1894 浏览

c# - 如何使用存储库模式处理子实体的分页?

我正在学习领域驱动设计。我目前正在尝试用 C# 编写一个简单的应用程序,使用 DDD 设计它。这个应用程序有一个聚合根 A,它可以包含 0..n 个子实体 B。这可以用以下方式表示:

有一个存储库:

但是,我想在为给定的 A 实例呈现 B 子实体时添加分页。我该怎么做呢?我能想到的最好的方法是将 A 和 ARepository 更改为:

当然,这会起作用,但我会失去域模型的简单性和优雅性。

如何使用存储库模式处理子实体的分页的最佳实践是什么?我不是在寻找如何使用特定的库等来处理这个问题,而是在寻找一种在“模式级别”上处理它的方法。

0 投票
3 回答
2178 浏览

language-agnostic - DDD:通过身份引用聚合根中的实体

当我们只从 URL 参数中获取它们的身份时,我一直在寻找正确的方法来引用位于聚合根中的实体。我问了一个先前的问题,该问题最终集中在value objects上,所以我从这里的另一个示例开始。

假设我们要修改 anOrderLine内部Order

  • 用户转到一个页面,他可以在其中看到订单摘要及其所有订单行。
  • 用户单击订单行旁边的编辑按钮。
  • 他被引导到edit-order-line?orderId=x&orderLineId=y

现在,如果我需要更新 OrderLine 中的数量,我可以这样做:

但是,我对将责任留给教团通过 Id 检索其自身的部分的想法感到不太自在。我对这个主题的看法是,在领域内,我们应该只与对象交谈,而不是与 Ids 交谈。ID 不是通用语言的一部分,我相信它们应该存在于域之外,例如在控制器中。

我会对以下内容更有信心:

虽然我也不喜欢直接与实体管理器交互的想法。我觉得我正在绕过 Repository 和 Aggregate Root 职责(因为我可以直接与 OrderLine 交互)。

你如何解决这个问题?