聚合根内集合实体更新的最佳实践方案探讨
电商购物车聚合根总价同步的设计难题
领域示例
假设我们处于电商领域,存在购物车(ShoppingCart)和购物车内的商品(ShoppingCartItem)两个实体。其中购物车作为聚合根(Aggregate Root),商品仅为普通实体。购物车包含一个由旗下所有商品价格计算得出的总价(TotalPrice)。
添加商品后支持修改数量,此时购物车总价需要同步重新计算,这是设计中的难点,我不确定该如何实现最优的代码设计。
当前代码实现
ShoppingCart
public class ShoppingCart: IAggregateRoot { private List<ShoppingCartItem> _items = new(); // 其他属性和工厂方法 public IReadOnlyCollection<ShoppingCartItem> ShoppingCartItems => _items.AsReadOnly(); public decimal TotalPrice { get; private set; } public void AddShoppingCartItem(ShoppingCartItem shoppingCartItem) { _shoppingCartItems.Add(shoppingCartItem); CalculateTotalPrice(); } public void RemoveShoppingCartItem(ShoppingCartItem shoppingCartItem) { _shoppingCartItems.Remove(shoppingCartItem); CalculateTotalPrice(); } private void CalculateTotalPrice() { TotalPrice = _shoppingCartItems.Sum(x => x.TotalPrice); } }
ShoppingCartItem
public class ShoppingCartItem { public int Id { get; private set; } public decimal Quantity { get; private set; } public decimal UnitPrice { get; private set; } public decimal TotalPrice { get; private set; } // 其他属性和工厂方法 public void SetQuantity(decimal quantity) { Quantity = quantity; RecalculatePrices(); } private void RecalculatePrices() { TotalPrice = Quantity * UnitPrice; } }
当前代码的问题:未触发购物车总价重算
修改购物车商品时,仅该商品自身的价格会重新计算,变更通过仓储(Repository)持久化到数据库,但购物车的总价因未触发重算而不准确。
对应的命令处理器实现如下:
public class UpdateShoppingCartItemCommandHandler : IRequestHandler<UpdateShoppingCartItemCommand> { private readonly IShoppingCartRepository _shoppingCartRepository; public UpdateShoppingCartItemCommandHandler(IShoppingCartRepository shoppingCartRepository) { _shoppingCartRepository= shoppingCartRepository; } public async Task<Unit> Handle(UpdateShoppingCartItemCommand request, CancellationToken cancellationToken) { var shoppingCart = await _shoppingCartRepository.GetById(request.ShoppingCartId); var shoppingCartItem = shoppingCart.ShoppingCartItems.First(x => x.Id == request.ShoppingCartItemId); shoppingCartItem.SetQuantity(request.Model.Quantity); await _shoppingCartRepository.Update(shoppingCart); return Unit.Value; } }
可行方案1:在购物车内添加商品更新逻辑
public class ShoppingCart: IAggregateRoot { // 已有的代码... public void UpdateShoppingCartItemQuantity(int shoppingCartItemId, decimal quantity) { var shoppingCartItem = _shoppingCartItems.First(x => x.Id == shoppingCartItemId); shoppingCartItem.SetQuantity(quantity); CalculateTotalPrice(); } }
但实际场景中ShoppingCartItem会有大量属性修改方法,为每个方法都在ShoppingCart中新增对应方法显然不合理,因此不认可该方案。
可行方案2:主动触发重算
将ShoppingCart的CalculateTotalPrice方法设为公共方法:
public class ShoppingCart: IAggregateRoot { public void CalculateTotalPrice() { TotalPrice = _shoppingCartItems.Sum(x => x.TotalPrice); } }
然后在命令处理器中手动触发计算:
public class UpdateShoppingCartItemCommandHandler : IRequestHandler<UpdateShoppingCartItemCommand> { // 已有的代码... public async Task<Unit> Handle(UpdateShoppingCartItemCommand request, CancellationToken cancellationToken) { var shoppingCart = await _shoppingCartRepository.GetById(request.ShoppingCartId); var shoppingCartItem = shoppingCart.ShoppingCartItems.First(x => x.Id == request.ShoppingCartItemId); shoppingCartItem.SetQuantity(request.Model.Quantity); shoppingCart.CalculateTotalPrice(); await _shoppingCartRepository.Update(shoppingCart); return Unit.Value; } }
该方案的问题是:如果商品数量在多处被修改,开发者很容易忘记调用CalculateTotalPrice,导致总价错误。
恳请各位分享经验,给出最优解决方案,感谢!
内容的提问来源于stack exchange,提问作者user19924124
相关产品推荐
相关产品推荐

