我阅读了 Evans、Nilsson 和 McCarthy 等人的著作,并了解领域驱动设计背后的概念和推理;但是,我发现很难将所有这些组合到现实世界的应用程序中。 缺乏完整的例子让我摸不着头脑。 我找到了很多框架和简单的示例,但到目前为止还没有真正演示如何遵循 DDD 构建真正的业务应用程序。
以典型的订单管理系统为例,以订单取消为例。 在我的设计中,我可以看到带有 CancelOrder 方法的 OrderCancellationService,该方法接受订单 # 和原因作为参数。 然后它必须执行以下“步骤”:
当然,所有这些都应该发生在一个事务中,并且任何操作都不允许独立发生。我的意思是,如果我取消订单,我必须恢复信用卡交易,我不能取消订单而不执行此步骤。在我看来,这建议更好的封装,但我不想在我的域对象(订单)中依赖 CreditCardService,所以这似乎是域服务的责任。
我正在寻找有人向我展示代码示例,如何可以/应该“组装”。代码背后的思考过程将有助于我自己将所有点联系起来。 谢谢!
稍微不同的看法:
//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);
}
}
}
备注:
您的域服务可能如下所示。请注意,我们希望在实体中保留尽可能多的逻辑,从而保持域服务的精简。另请注意,不直接依赖信用卡或审核员实施 (DIP)。我们仅依赖于域代码中定义的接口。稍后可以将实现注入到应用程序层中。应用程序层还将负责按编号查找订单,更重要的是,负责在事务中包装“取消”调用(异常时回滚)。
class OrderCancellationService {
private readonly ICreditCardGateway _creditCardGateway;
private readonly IAuditor _auditor;
public OrderCancellationService(
ICreditCardGateway creditCardGateway,
IAuditor auditor) {
if (creditCardGateway == null) {
throw new ArgumentNullException("creditCardGateway");
}
if (auditor == null) {
throw new ArgumentNullException("auditor");
}
_creditCardGateway = creditCardGateway;
_auditor = auditor;
}
public void Cancel(Order order) {
if (order == null) {
throw new ArgumentNullException("order");
}
// get current user through Ambient Context:
// http://blogs.msdn.com/b/ploeh/archive/2007/07/23/ambientcontext.aspx
if (!CurrentUser.CanCancelOrders()) {
throw new InvalidOperationException(
"Not enough permissions to cancel order. Use 'CanCancelOrders' to check.");
}
// try to keep as much domain logic in entities as possible
if(!order.CanBeCancelled()) {
throw new ArgumentException(
"Order can not be cancelled. Use 'CanBeCancelled' to check.");
}
order.Cancel();
// this can throw GatewayException that would be caught by the
// 'Cancel' caller and rollback the transaction
_creditCardGateway.RevertChargesFor(order);
_auditor.AuditCancellationFor(order);
}
}