我正在为一家寻找供应商为员工搬迁提供服务的公司开展一个项目。这些服务是搬运工不具备专业知识的事情,例如准备钢琴或运输或为贵重物品建造板条箱。
在这个域中,一个订单有 1:many Locations。
在搬家行业中,订单经常在不断变化,直到供应商执行要求他提供的服务。因此,在我们的模型中,我们有一些适用于订单和地点的状态(例如已提交、已取消、暂停)。
有一些非常简单的业务规则适用于此。这是一个示例:
- 当订单被搁置时,所有位置都被搁置。
- 如果其父订单处于暂停状态,则不能取消暂停位置。
等等。从这些规则中,对我来说很明显这形成了一个聚合根边界。因此,我有一个MyClient.Statuses.Order
聚合,Statuses
上下文/服务的名称/您想调用的名称在哪里:
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;
}
}
这两个对象(订单、位置)在其他上下文中都有 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();
}
}