Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/maven/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Domain driven design 用DDD连接点_Domain Driven Design_Ddd Repositories_Ddd Service - Fatal编程技术网

Domain driven design 用DDD连接点

Domain driven design 用DDD连接点,domain-driven-design,ddd-repositories,ddd-service,Domain Driven Design,Ddd Repositories,Ddd Service,我阅读了Evans、Nilsson和McCarthy等著作,了解领域驱动设计背后的概念和推理;然而,我发现很难将所有这些结合到实际应用程序中。缺乏完整的例子让我抓狂。我已经找到了很多框架和简单的例子,但到目前为止,还没有真正演示如何按照DDD构建真正的业务应用程序 以典型的订单管理系统为例,以订单取消为例。在我的设计中,我可以看到带有CancelOrder方法的OrderCancellationService,该方法接受订单和原因作为参数。然后必须执行以下“步骤”: 验证当前用户是否具有取消订单

我阅读了Evans、Nilsson和McCarthy等著作,了解领域驱动设计背后的概念和推理;然而,我发现很难将所有这些结合到实际应用程序中。缺乏完整的例子让我抓狂。我已经找到了很多框架和简单的例子,但到目前为止,还没有真正演示如何按照DDD构建真正的业务应用程序

以典型的订单管理系统为例,以订单取消为例。在我的设计中,我可以看到带有CancelOrder方法的OrderCancellationService,该方法接受订单和原因作为参数。然后必须执行以下“步骤”:

  • 验证当前用户是否具有取消订单的必要权限
  • 从OrderRepository中检索具有指定订单的订单实体
  • 验证是否可以取消订单(服务是否应询问订单状态以评估规则,或者订单是否具有封装规则的CanCancel属性?)
  • 通过调用Order.Cancel(原因)更新订单实体的状态
  • 将更新后的订单保存到数据存储中
  • 联系CreditCardService以恢复已处理的任何信用卡费用
  • 为操作添加审核条目
  • 当然,所有这些都应该在事务中发生,并且不允许任何操作独立发生。我的意思是,如果我取消订单,我必须恢复信用卡交易,我不能取消,也不能执行此步骤。依我看,这意味着更好的封装,但我不想在我的域对象(Order)中依赖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);
            }
        }
    }
    
    注:

    • 一般来说,我只看到当一个命令/用例涉及多个聚合的状态更改时,服务的需要。例如,如果我需要调用Customer聚合和Order上的方法,那么我将创建域服务OrderCancellationService来调用这两个聚合上的方法
    • 应用层在基础设施(支付网关)和域之间进行协调。与域对象一样,域服务应该只关注域逻辑,而不了解支付网关等基础设施;即使您已经使用自己的适配器对其进行了抽象
    • 关于权限,我将使用它从逻辑本身中提取出来。正如您在我的示例中看到的,我已经向CancelOrder方法添加了一个属性。您可以在该方法上使用拦截器来查看当前用户(我将在Thread.CurrentPrincipal上设置)是否具有该权限
    • 关于审计,您只是简单地说了“操作审计”。如果您只是指一般的审计(即所有应用程序服务调用),那么我将再次在方法上使用拦截器,记录用户、调用了哪个方法以及使用了哪些参数。但是,如果您的意思是专门针对订单/付款的取消进行审计,那么请执行类似于Dmitry示例的操作

    为什么我不希望“查找”、事务管理和调用在服务中保持更改?这似乎可以确保每次都能正确使用。查找-也许吧。事务管理不属于域服务,它通常在应用层(域服务的调用者)实现“持久化更改”的调用由ORM或UnitOfWork处理,因为我们正在更改现有对象,在NHibernate情况下不需要显式调用。其想法是尽可能地保持域代码的持久性不可知性。是的,我会使用OrderRepository和UoW尽可能地保持域的持久性不可知性,但是没有任何东西可以阻止应用程序代码在不保持对Order实体的更改的情况下调用您的取消服务。作为不可知论者,我认为直到现在我们才使用NHibernate并不重要,因此基于ORM的任何假设都是无效的。我还应该提到,我总是在团队环境中工作,在团队环境中,代码必须是高度有意的,不能对上游编码者(调用方)做出假设,比如他们是否执行了所有的规则。我的印象是,这就是域服务在设计中扮演的角色,而应用层就是UI。我弄错了吗?是的,那很好。关键是“应用程序服务”是域模型/服务的入口点。此级别是指已编排事务的级别。我个人也在这一级别管理用户权限,因此我的模型只关注域逻辑。那么该特定资源的用户权限(orderId)呢?对于数据资源的权限,而不是功能/事务,我会将其视为业务规则,并像任何其他规则一样检查代码
    //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);
            }
        }
    }