Domain driven design DDD-聚合中子对象的修改
我很难找到处理相当复杂情况的最佳方法。我见过很多类似的问题,但没有一个能让我满意地解决这个问题 使用多个订单行(子实体)创建订单(聚合根)。根据业务规则,每个订单行必须在订单的生命周期内保持相同的标识。订单行具有许多(20+)属性,在订单被视为“锁定”之前,可以相当频繁地进行变异。此外,还有一些不变量必须在根级别强制执行;例如,每个订单行都有一个数量,订单的总数量不能超过X 我不确定在考虑对订单行进行更改时如何对该场景建模。我有4个选择,我可以想象,但没有一个是令人满意的: 1) 当需要修改订单行时,请使用root提供的引用进行修改。但是我失去了在根中检查不变逻辑的能力Domain driven design DDD-聚合中子对象的修改,domain-driven-design,aggregateroot,Domain Driven Design,Aggregateroot,我很难找到处理相当复杂情况的最佳方法。我见过很多类似的问题,但没有一个能让我满意地解决这个问题 使用多个订单行(子实体)创建订单(聚合根)。根据业务规则,每个订单行必须在订单的生命周期内保持相同的标识。订单行具有许多(20+)属性,在订单被视为“锁定”之前,可以相当频繁地进行变异。此外,还有一些不变量必须在根级别强制执行;例如,每个订单行都有一个数量,订单的总数量不能超过X 我不确定在考虑对订单行进行更改时如何对该场景建模。我有4个选择,我可以想象,但没有一个是令人满意的: 1) 当需要修改订单
var orderLine = order.GetOrderLine(id);
orderLine.Quantity = 6;
2) 调用订单上的方法。我可以应用所有不变的逻辑,但随后我不得不使用大量的方法来修改订单行的许多属性:
order.UpdateOrderLineQuantity(id, 6);
order.UpdateOrderLineDescription(id, description);
order.UpdateOrderLineProduct(id, product);
...
public void UpdateQuantity(int quantity, IOrderValidator orderValidator)
{
if(orderValidator.CanUpdateQuantity(this, quantity))
Quantity = quantity;
}
3) 如果我将OrderLine视为一个值对象,这可能会更容易,但它必须根据业务需求维护相同的标识
4) 我可以为不影响不变量的修改获取对订单行的引用,并为那些影响不变量的修改遍历订单。但如果不变量受大多数订单行属性的影响呢?这个异议是假设性的,因为只有少数属性可以影响不变量,但随着我们发现更多的业务逻辑,这些属性会发生变化
任何建议都非常感谢…如果我很紧张,请随时告诉我
与2相比,4的一个缺点是缺乏一致性。在某些情况下,在更新订单行项目方面保持一定程度的一致性可能是有益的。某些更新是通过订单完成的,而其他更新是通过订单行项目完成的,这一点可能还不清楚。此外,如果订单行具有20+个属性,这可能表明这些属性之间存在分组的可能性,从而导致订单行上的属性更少。总的来说,只要确保操作原子化、一致性并与通用语言保持一致,方法2或4就可以了。有第五种方法可以做到这一点。您可以启动(例如,
QuantityUpdateEvent(订单、产品、金额)
)。通过查看订单行列表,让聚合在内部处理它,选择具有匹配产品的订单行,并更新其数量(或者将操作委托给订单行
,这更好)域事件是最健壮的解决方案
但是,如果这太过分了,您也可以使用参数对象模式执行#2的变化-在实体根上有一个ModfiyOrderItem函数。提交一个新的、更新的订单项目,然后订单在内部验证这个新对象并进行更新
所以你典型的工作流程会变成
var orderItemToModify = order.GetOrderItem(id);
orderItemToModify.Quantity = newQuant;
var result = order.ModifyOrderItem(orderItemToModfiy);
if(result == SUCCESS)
{
//good
}
else
{
var reason = result.Message; etc
}
这里的主要缺点是它允许程序员修改项目,但不提交它,也不实现它。但是,它很容易扩展和测试。如果您的项目很小,并且希望避免域事件的复杂性,那么这里有另一个选项。创建一个处理订单规则的服务,并将其传递给OrderLine上的方法:
order.UpdateOrderLineQuantity(id, 6);
order.UpdateOrderLineDescription(id, description);
order.UpdateOrderLineProduct(id, product);
...
public void UpdateQuantity(int quantity, IOrderValidator orderValidator)
{
if(orderValidator.CanUpdateQuantity(this, quantity))
Quantity = quantity;
}
CanUpdateQuantity将当前订单行和新数量作为参数。它应该查找订单,并确定更新是否导致订单总量发生冲突。(您必须确定如何处理更新冲突。)
如果您的项目很小,并且不需要域事件的复杂性,那么这可能是一个很好的解决方案
这种技术的一个缺点是,您正在将订单的验证服务传递到OrderLine,而OrderLine实际上不属于该服务。相反,引发域事件会将订单逻辑移出订单行。然后,订单行就可以对世界说,“嘿,我在改变我的数量。”订单验证逻辑可以在处理程序中进行。使用DTO怎么样
public class OrderLineDto
{
public int Quantity { get; set; }
public string Description { get; set; }
public int ProductId { get; set; }
}
public class Order
{
public int? Id { get; private set; }
public IList<OrderLine> OrderLines { get; private set; }
public void UpdateOrderLine(int id, OrderLineDto values)
{
var orderLine = OrderLines
.Where(x => x.Id == id)
.FirstOrDefault();
if (orderLine == null)
{
throw new InvalidOperationException("OrderLine not found.");
}
// Some domain validation here
// throw new InvalidOperationException("OrderLine updation is not valid.");
orderLine.Quantity = values.Quantity;
orderLine.Description = values.Description;
orderLine.ProductId = values.ProductId;
}
}
public类OrderLineDto
{
公共整数数量{get;set;}
公共字符串说明{get;set;}
public int ProductId{get;set;}
}
公共阶级秩序
{
公共int?Id{get;private set;}
公共IList命令行{get;private set;}
public void UpdateOrderLine(int-id,OrderLineDto值)
{
var orderLine=订单行
.其中(x=>x.Id==Id)
.FirstOrDefault();
if(orderLine==null)
{
抛出新的InvalidOperationException(“未找到医嘱行”);
}
//这里有一些域验证
//抛出新的InvalidOperationException(“订单行更新无效”);
orderLine.Quantity=value.Quantity;
orderLine.Description=values.Description;
orderLine.ProductId=values.ProductId;
}
}
- 这里唯一的问题是,
属性具有公共getter,此类的用户可以将项添加到集合中。我能想到的唯一方法是隐藏getter并添加新的getter,它将返回colOrderLines