我遇到了很常见的问题(我认为)。 “REST”API、.NET 8、MediatR CQRS、EF Core
假设我们有以下课程:
public class OrderHeader : AuditableEntity
{
public required string AddressId { get; init; }
public Address Address { get; set; } = null!;
//...
}
public class Address : AuditableEntity
{
public required string Street { get; init; }
//...
}
假设订单已完成。一切都已保存并且很好。然后,我对
PUT
对象执行 Address
命令,该对象是已完成的 Order
的导航属性,例如我们更改街道。
然后在我的前端,即使订单完成,在历史视图中,该地址也会被新的地址覆盖,我们都知道这种情况一定不会发生。
到目前为止,我已经通过以下方式处理了这样的问题:
public class OrderHeader : AuditableEntity
{
public required string AddressId { get; init; }
public Address Address { get; set; } = null!;
public string PersistentAddressStreet { get; set; } = "";
//...
}
在
POST CreateOrderHeaderQueryHandler
中,我只需将所有 Address
属性映射到 PersistantAddressProperty
属性,从而永久保存地址。
有没有更好、更干净、更常用的方法来实现同样的事情?我真的不太了解 DDD,这也许会有所帮助,或者也许我的解决方案很好,因为它正在工作?
我认为你的问题源于你的设计。订单应仅包含地址的快照(就像产品价格一样)。我认为它应该是一个值对象而不是一个实体。无需包含导航属性,因为它不是实体或不在订单聚合边界内。
您可以定义一个聚合来表示用户的个人资料及其地址簿。但只有这些地址的快照应该保留在订单聚合上。当地址变更时,若要更新尚未确定的订单,您应保留地址 ID。在 AddressChangedIntegrationEventHandler 中,使用该 ID 更新订单聚合中的地址值(尚未最终确定)。
有些人会为未最终确定的订单创建 BasketAggregate,其属性引用其他实体(如产品)。确保预发票(或账单)显示实时信息,例如库存产品的价格和可用性。但创建订单后,所有信息都将被存储。