在此域中,订单具有1:多个位置.
在移动行业中,订单经常不断变化,直到供应商执行他所要求的服务为止.因此,在我们的模型中,我们有一些适用于订单和位置的状态(例如已提交,已取消,已暂停).
这里有一些非常简单的业务规则.这是一个抽样:
>当订单处于暂停状态时,所有位置都将处于暂停状态.
>如果其父订单处于暂停状态,则无法取消暂停.
等等.根据这些规则,我觉得这形成了一个聚合根边界.因此,我有一个MyClIEnt.Statuses.Order聚合,其中状态是上下文/服务的名称/您要调用的任何内容:
public class Order { private GuID _ID; private OrderStatus _status; public voID PlaceOnHold() { if (_status == OrderStatus.Cancelled) // throw exception _status = OrderStatus.OnHold; Locations.ForEach(loc => loc.PlaceOnHold()); } public voID PlaceLocationOnHold(GuID ID) { if (_status == OrderStatus.Cancelled) // throw exception Locations.Single(loc => loc.ID == ID).PlaceOnHold(); } // etc... private Location[] Locations;}internal class Location { public GuID ID; public LocationStatus Status; public voID PlaceOnHold() { // It's ok for a cancelled location on a non-cancelled order,// but a Location cannot be placed On Hold if it's Cancelled so // just ignore it if (Status == LocationStatus.Cancelled) return; Status = LocationStatus.OnHold; }}
这两个对象(Order,Location)在其他上下文中都有GUID ID(例如,对于没有状态转换的基于CRUD的属性).所以现在我们终于得到了我的问题:
如何编写命令和处理程序以保持位置?
我想保持这个DRY和面向服务的东西以最小化耦合,但是在两个实体之间仅在一个地方保持父子关系真的很难.
选项1 – 单个位置ID:
public class PlaceLocationOnHold_V1 { public Readonly GuID ID;}public class PlaceLocationOnHold_V1Handler { public voID Handle(PlaceLocationOnHold_V1 command) { // This is typically a no-no. Should only fetch by OrderID: var aggregate = _repository.GetByLocationID(command.ID); aggregate.PlaceLocationOnHold(command.ID); _repository.Save(); }}
选项2 – 订单ID和位置ID:
public class PlaceLocationOnHold_V2 { public Readonly GuID OrderID; // This feels redundant public Readonly GuID LocationID;}public class PlaceLocationOnHold_V2Handler { public voID Handle(PlaceLocationOnHold_V2 command) { var aggregate = _repository.GetByID(command.OrderID); aggregate.PlaceLocationOnHold(command.LocationID); _repository.Save(); }}
选项3 – 带有类的单个参数,封装了“一个位置,属于一个订单”
public class LocationIDentity { public GuID ID; public GuID OrderID;}public class PlaceLocationOnHold_V3 { public Readonly LocationIDentity Location;}public class PlaceLocationOnHold_V3Handler { public voID Handle(PlaceLocationOnHold_V3 command) { var aggregate = _repository.GetByID(command.Location.OrderID); aggregate.PlaceLocationOnHold(command.Location.ID); _repository.Save(); }}解决方法 查看 Effective Aggregate Design上的Vaughn Vernon文章.具体来说,Part 2 – 有一些关于建模聚合的良好信息,这些聚合相互通信.
您的设计中缺少的要点是,这些都是您已经提到的AR – 它们是全局可识别的.所以他们应该通过ID引用彼此.订单不应包含子集合.
因此,您的Order类将包含LocationIDs集合,您的Location将具有OrderID.
public class Order{ private GuID _ID; private OrderStatus _status; private GuID[] _locationIDs; //...}public class Location{ private GuID _ID; private GuID _orderID; //...}
一旦你正确地解决了这个问题,选项#1就有意义了.由于Location本身就是一个AR,您可以实例化它并直接在其上调用PlaceOnHold,而无需通过Order AR.
至于一个AR的变化逐渐渗透到其他AR的情况(即暂停下单也使得所有位置也被搁置),您可以使用域事件或最终一致性.
public class Order{ //... private instance variables public voID PlaceOnHold() { if (_status == OrderStatus.Cancelled) // throw exception _status == Orderstatus.OnHold; DomainEvents.Handle(new OrderPlacedOnHold(_ID)); // handle this,look up the related locations and call PlaceOnHold on each of them) }}
对于您可能尝试删除某个位置的保留但是该命令处于暂停状态以使该 *** 作非法的情况,您可以在命令处理程序中实例化Order对象并将其传递到Location的RemoveFromHold方法.弗农提到了这一点并重申了这一点,仅仅因为每个事务只能改变一个AR并不意味着你不能在事务中实例化多个AR.
public class RemoveHoldFromLocation : IHandler<RemoveHoldFromLocationCommand>{ public voID Execute(RemoveHoldFromLocationCommand cmd) { var location = locationRepo.Get(cmd.LocationID); var order = orderRepo.Get(location.GetorderID()); location.RemoveHold(order.GetStatus()); }}public class Location{ //... private instance variables,etc. public voID RemoveHold(OrderStatus orderStatus) { if (orderStatus == OrderStatus.OnHold) // throw Exception _status == LocationStatus.OnHold; }}
这只是伪代码所以原谅错别字等.类似的代码示例在Vernon pdf中.
总结以上是内存溢出为你收集整理的domain-driven-design – 如何为在一个上下文中而不是在另一个上下文中的聚合根的实体编写命令?全部内容,希望文章能够帮你解决domain-driven-design – 如何为在一个上下文中而不是在另一个上下文中的聚合根的实体编写命令?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)