Domain driven design 用DDD连接点
我阅读了Evans、Nilsson和McCarthy等著作,了解领域驱动设计背后的概念和推理;然而,我发现很难将所有这些结合到实际应用程序中。缺乏完整的例子让我抓狂。我已经找到了很多框架和简单的例子,但到目前为止,还没有真正演示如何按照DDD构建真正的业务应用程序 以典型的订单管理系统为例,以订单取消为例。在我的设计中,我可以看到带有CancelOrder方法的OrderCancellationService,该方法接受订单和原因作为参数。然后必须执行以下“步骤”:Domain driven design 用DDD连接点,domain-driven-design,ddd-repositories,ddd-service,Domain Driven Design,Ddd Repositories,Ddd Service,我阅读了Evans、Nilsson和McCarthy等著作,了解领域驱动设计背后的概念和推理;然而,我发现很难将所有这些结合到实际应用程序中。缺乏完整的例子让我抓狂。我已经找到了很多框架和简单的例子,但到目前为止,还没有真正演示如何按照DDD构建真正的业务应用程序 以典型的订单管理系统为例,以订单取消为例。在我的设计中,我可以看到带有CancelOrder方法的OrderCancellationService,该方法接受订单和原因作为参数。然后必须执行以下“步骤”: 验证当前用户是否具有取消订单
我希望有人能向我展示如何“组装”代码示例。代码背后的思考过程将有助于让我为自己连接所有的点。谢谢 您的域服务可能如下所示。注意,我们希望在实体中保留尽可能多的逻辑,保持域服务精简。还要注意的是,它不直接依赖于信用卡或审计员的实现()。我们只依赖于域代码中定义的接口。稍后可以将实现注入应用程序层。应用层还将负责按编号查找订单,更重要的是,在事务中包装“取消”调用(回滚异常)
对此有一个稍微不同的看法:
//UI
public class OrderController
{
private readonly IApplicationService _applicationService;
[HttpPost]
public ActionResult CancelOrder(CancelOrderViewModel viewModel)
{
_applicationService.CancelOrder(new CancelOrderCommand
{
OrderId = viewModel.OrderId,
UserChangedTheirMind = viewModel.UserChangedTheirMind,
UserFoundItemCheaperElsewhere = viewModel.UserFoundItemCheaperElsewhere
});
return RedirectToAction("CancelledSucessfully");
}
}
//App Service
public class ApplicationService : IApplicationService
{
private readonly IOrderRepository _orderRepository;
private readonly IPaymentGateway _paymentGateway;
//provided by DI
public ApplicationService(IOrderRepository orderRepository, IPaymentGateway paymentGateway)
{
_orderRepository = orderRepository;
_paymentGateway = paymentGateway;
}
[RequiredPermission(PermissionNames.CancelOrder)]
public void CancelOrder(CancelOrderCommand command)
{
using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create())
{
Order order = _orderRepository.GetById(command.OrderId);
if (!order.CanBeCancelled())
throw new InvalidOperationException("The order cannot be cancelled");
if (command.UserChangedTheirMind)
order.Cancel(CancellationReason.UserChangeTheirMind);
if (command.UserFoundItemCheaperElsewhere)
order.Cancel(CancellationReason.UserFoundItemCheaperElsewhere);
_orderRepository.Save(order);
_paymentGateway.RevertCharges(order.PaymentAuthorisationCode, order.Amount);
}
}
}
注:
- 一般来说,我只看到当一个命令/用例涉及多个聚合的状态更改时,域服务的需要。例如,如果我需要调用Customer聚合和Order上的方法,那么我将创建域服务OrderCancellationService来调用这两个聚合上的方法
- 应用层在基础设施(支付网关)和域之间进行协调。与域对象一样,域服务应该只关注域逻辑,而不了解支付网关等基础设施;即使您已经使用自己的适配器对其进行了抽象
- 关于权限,我将使用它从逻辑本身中提取出来。正如您在我的示例中看到的,我已经向CancelOrder方法添加了一个属性。您可以在该方法上使用拦截器来查看当前用户(我将在Thread.CurrentPrincipal上设置)是否具有该权限
- 关于审计,您只是简单地说了“操作审计”。如果您只是指一般的审计(即所有应用程序服务调用),那么我将再次在方法上使用拦截器,记录用户、调用了哪个方法以及使用了哪些参数。但是,如果您的意思是专门针对订单/付款的取消进行审计,那么请执行类似于Dmitry示例的操作
//UI
public class OrderController
{
private readonly IApplicationService _applicationService;
[HttpPost]
public ActionResult CancelOrder(CancelOrderViewModel viewModel)
{
_applicationService.CancelOrder(new CancelOrderCommand
{
OrderId = viewModel.OrderId,
UserChangedTheirMind = viewModel.UserChangedTheirMind,
UserFoundItemCheaperElsewhere = viewModel.UserFoundItemCheaperElsewhere
});
return RedirectToAction("CancelledSucessfully");
}
}
//App Service
public class ApplicationService : IApplicationService
{
private readonly IOrderRepository _orderRepository;
private readonly IPaymentGateway _paymentGateway;
//provided by DI
public ApplicationService(IOrderRepository orderRepository, IPaymentGateway paymentGateway)
{
_orderRepository = orderRepository;
_paymentGateway = paymentGateway;
}
[RequiredPermission(PermissionNames.CancelOrder)]
public void CancelOrder(CancelOrderCommand command)
{
using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create())
{
Order order = _orderRepository.GetById(command.OrderId);
if (!order.CanBeCancelled())
throw new InvalidOperationException("The order cannot be cancelled");
if (command.UserChangedTheirMind)
order.Cancel(CancellationReason.UserChangeTheirMind);
if (command.UserFoundItemCheaperElsewhere)
order.Cancel(CancellationReason.UserFoundItemCheaperElsewhere);
_orderRepository.Save(order);
_paymentGateway.RevertCharges(order.PaymentAuthorisationCode, order.Amount);
}
}
}