我正在尝试遵循 DDD,但感觉我错过了一些东西。我的解决方案是这样的:
Order
和 OrderItem
类的域项目(这些类具有包含一些业务逻辑的实例方法,如 CanChangeOrder
等)Orders
和 OrderItems
)我需要您的建议:
假设客户编辑他的订单(添加 1 件新商品、更改现有商品数量并删除 2 件商品)。
在(基础设施项目)中,我获取
Order
和OrderItems
,转换为订单域对象(使用automapper
)并返回到应用层的调用者方法。
var dbOrder = await _dbContext.Orders.Where(o => o.OrderId == orderId && o.DeletedDateTimeUtc == null).Include(o => o.OrderItems.Where(i => i.DeletedDateTimeUtc == null)).SingleOrDefaultAsync();
var domainOrder = _mapper.Map<Domain.Entities.Order>(dbOrder);
return Result.Ok(domainOrder);
(应用层)订单域对象有一个实例方法,用于验证订单是否可以编辑。如果为 true,我将添加、编辑、删除 Order 对象中的项目,现在将修改后的 Order 对象(包括 OrderItems)发送到持久层(基础设施项目)。
var orderResult = await _orderRepo.Get(request.OrderId);
if (!orderResult.Value.CanOrderBeUpdated())
return Result.Fail("Sorry, order can not be changed now.");
else
update domain model based on the user request
_orderRepo.Update(domainOrderModel)
(基础设施层)这里我再次将我的
Order
域模型转换为EF Core实体Order
模型。但所有 OrderItem
都被视为新项目,因为 EF Core 没有跟踪现有项目,从而在 OrderItems
表中产生新行。为了避免这种情况,我在下面的 Update
方法中进行了检查:
public void Update(Domain.Entities.Order order)
{
var dbOrder = _mapper.Map<Infrastructure.Entities.Order>(order);
// to instruct ef to insert or update an order item
foreach (var item in dbOrder.OrderItems)
{
if (item.OrderId > 0)
_dbContext.Entry(item).State = EntityState.Modified;
}
_dbContext.Orders.Update(dbOrder);
}
我觉得这种方法效率不高(你觉得吗?),请提出改进意见。
谢谢你
我觉得这种方法效率不高(你觉得吗?),请提出改进意见。
答案取决于您如何看待持久层。
存储库的概念是在 ORM 库推广之前发明的。那时,基础设施层将是开发人员将 OOP (
Order.Update()
) 转换为 SQL (UPDATE Orders SET ... WHERE id = ...
) 的地方。实际上,ORM 库是在开发人员尝试概括他们在 SQL 基础架构之上实现的对象存储库模式后构建的。
如果基础设施层的目的是无条件存储和恢复域状态,并且您并不真正关心数据库中数据的结构(域优先),那么是的,这是一种非常低效的方法。在这种情况下,使用 EF 实体作为您的域模型,并让 EF 本身(库)作为您的基础设施层。 DbContext 既充当存储库又充当工作单元:
namespace Domain;
public class UpdateOrderUseCase(MyDbContext database) // .NET 8 notation for consistency
{
public void Execute(UpdateOrderDto payload)
{
var order = database.Orders.Find(payload.id) ?? throw new NotFoundException();
order.CanUpdate || throw new ConflictException();
// update order from payload
database.SaveChanges();
}
}
注意:上面的代码是一个意图示例,请注意您的实际实现。
但这会给您的领域模型带来一些限制:使用 EF 添加业务功能可能会很困难,因为库需要实体在关系世界中进行转换。您最终可能会拥有一个贫乏的领域模型,并将一些业务规则放入服务类中。
如果您希望拥有与业务模型结构不同的持久性模式,并且想要一个非贫乏的域模型,那么您需要这些转换。由于业务模型和持久性模型存在结构差异,因此进行此映射将具有更多的感知价值。
愚蠢的代码示例,允许将订单详细信息和商品详细信息存储在单独的数据库中,省略两阶段提交模式和事务以提高可读性:
namespace Infrastructure;
public class OrderRepository(
OrderDbContext orders,
OrderItemsDbContext items,
) : IOrderRepository
{
public void Update(Domain.Entities.Order order)
{
var oder = orders.FindById(order.Id) ?? throw NotFoundException();
// update order details
orders.SaveChanges();
items.Items.Where(item => item.OrderId == order.id).ExecuteDelete();
items.Items.AddRange(order.Items.ProjectTo<ItemsEntity>());
items.SaveChanges();
}
}