Domain driven design 在一对多关系中从聚合更新值对象

Domain driven design 在一对多关系中从聚合更新值对象,domain-driven-design,Domain Driven Design,我最近跳入DDD,这个问题开始困扰我。例如,看看下面提到的场景: 假设用户在向任务聚合添加EstimationGenertry时出错,现在希望更正该错误。正确的做法是什么?值对象本质上没有标识符,它们由其结构标识。如果这是一个Web应用程序,那么我们必须将整个EstimationLogEntryvalue对象与新值一起作为请求参数发送,以便用新值替换旧值对象。EstimationGenetry应该是一个实体吗?这取决于具体情况。如果是一系列的估计,每次都会追加,那么很可能会设想一个只更新VO值

我最近跳入DDD,这个问题开始困扰我。例如,看看下面提到的场景:


假设用户在向
任务
聚合添加
EstimationGenertry
时出错,现在希望更正该错误。正确的做法是什么?值对象本质上没有标识符,它们由其结构标识。如果这是一个Web应用程序,那么我们必须将整个
EstimationLogEntry
value对象与新值一起作为请求参数发送,以便用新值替换旧值对象。
EstimationGenetry
应该是一个实体吗?

这取决于具体情况。如果是一系列的估计,每次都会追加,那么很可能会设想一个只更新VO值的操作。这将使用VO语义(调用VO以在mem中使用特定属性上的更新值克隆自身),并且命令可以只是估计值(以及任务id)


如果您有一个VO数组,所有VO都在语义上应用于任务(而不仅仅是“最新”或其他内容)。。。这是另一回事。在这种情况下,您可能必须在请求中发送所有属性,并且还必须包含所有属性,但我要说的是,只更改一个属性可能意味着需要引用它们,这反过来意味着需要有一个实体而不是VO。

这真的取决于具体情况。如果是一系列的估计,每次都会追加,那么很可能会设想一个只更新VO值的操作。这将使用VO语义(调用VO以在mem中使用特定属性上的更新值克隆自身),并且命令可以只是估计值(以及任务id)


如果您有一个VO数组,所有VO都在语义上应用于任务(而不仅仅是“最新”或其他内容)。。。这是另一回事。在这种情况下,您可能必须在请求中发送所有属性,并且还必须包含所有属性,但我要说的是,只需要更改一个,可能意味着需要引用它们,这反过来意味着需要有一个实体而不是VO。

DDD强调普遍存在的语言,许多像这样的建模问题将直接从该语言中得到答案。 首先,如果有一个包含值对象的聚合,那么值对象很可能不是由用户直接创建的。也就是说,创建值对象的工厂位于聚合API上。值对象甚至可能直接从聚合状态派生,而不是从任何直接方法调用派生。在这种情况下,是否要放弃聚合并创建一个新的聚合?这可能有道理,取决于您的UL

在某些情况下,例如,如果您有不可变的值对象(基于您的UL),您可以简单地将新条目添加到日志条目中,以“反转”旧条目。银行账户和交易就是一个例子。如果银行账户是聚合根,交易是价值对象。如果交易输入错误,您只需编写一个反向交易即可将其作废


当然,您可能希望更新value对象,但这在您的UL中必须是有意义的,并且它的实现也必须围绕您的UL进行。例如,如果您有一个日程安排应用程序,并且聚合根是一个人的日程安排,而值对象是会议。如果用户错误地进入会议,聚合根用户应该做的是使旧会议无效(翻转一个标志,将其状态标记为已取消e.t.c)并创建一个新会议。这些操作适合您的日程安排应用程序。与您在上面所说的“更新条目”相同。

DDD强调普遍存在的语言,许多类似这样的建模问题将直接从该语言中获得答案。 首先,如果有一个包含值对象的聚合,那么值对象很可能不是由用户直接创建的。也就是说,创建值对象的工厂位于聚合API上。值对象甚至可能直接从聚合状态派生,而不是从任何直接方法调用派生。在这种情况下,是否要放弃聚合并创建一个新的聚合?这可能有道理,取决于您的UL

在某些情况下,例如,如果您有不可变的值对象(基于您的UL),您可以简单地将新条目添加到日志条目中,以“反转”旧条目。银行账户和交易就是一个例子。如果银行账户是聚合根,交易是价值对象。如果交易输入错误,您只需编写一个反向交易即可将其作废

当然,您可能希望更新value对象,但这在您的UL中必须是有意义的,并且它的实现也必须围绕您的UL进行。例如,如果您有一个日程安排应用程序,并且聚合根是一个人的日程安排,而值对象是会议。如果用户错误地进入会议,聚合根用户应该做的是使旧会议无效(翻转一个标志,将其状态标记为已取消e.t.c)并创建一个新会议。这些操作适合您的日程安排应用程序。与上面所说的“更新条目”相同