【问题标题】:Contextual Domain Driven Model Validation上下文域驱动模型验证
【发布时间】:2013-05-06 14:00:46
【问题描述】:

在我们的应用程序中,我们需要根据业务规则和当前用户的上下文来验证属性更新。我正在尝试确定进行验证的最佳方法,因为我认为域模型不应该知道当前用户。我们正常的授权与域是分开的,和这个场景不同。

这种验证应该在哪里进行?有没有更好的方法来处理它?域模型应该了解用户吗?任何帮助或意见表示赞赏。

简单示例: 我们有一个数量已批准的订单。只有特定的用户类型才能更新特定方向的数量。这是在域聚合中验证的正确方法吗?

public enum UserType
{
    ViewUserType,
    RequesterUserType,
    SupplierUserType
}

public class Order
{
    public int OrderId {get; private set}
    public int RequestedQuantity {get; private set}
    public int ApprovedQuantity {get; private set}

    public void RequestQuantity(int quantity, UserType userType)
    {
        if (userType == UserType.RequesterUserType)
        {
            this.RequestedQuantity = quantity;
        }
    }

    // Question: The direction that the approved quantity can change is a business rule
    // but directly deals with the context of the user.  Should the model know about the user
    // or should this validation be pulled out to either the application service, a model extension,
    // or maybe a specification?
    public void ApproveQuantity(int quantity, UserType userType)
    {
        if (userType == UserType.RequesterUserType)
        {
            if (quantity <= this.ApprovedQuantity)
            {
                // Requester type user can only update if lowering the approved quantity
                this.ApprovedQuantity = quantity;
            }
        }
        else if(userType == UserType.SupplierUserType)
        {
            if (quantity >= this.ApprovedQuantity)
            {
                // Supplier type user can only update if increasing the approved quantity
                this.ApprovedQuantity = quantity;
            }
        }
    }
}

【问题讨论】:

    标签: c# validation domain-driven-design domain-model application-layer


    【解决方案1】:

    与其拥有这种类似枚举的类型(UserType),为什么不将这些角色演变成完全成熟的对象呢?您关心的是用户扮演的角色,而不是特定用户。这将用户确实是供应商或请求者的身份验证和验证推送到上层(嗯,实际上是调用代码,在这种情况下可能是某种应用程序服务)。下面是一个非常粗略的第一次迭代:

    public class Order {
      public void RequestQuantity(int quantity, UserType userType)
      {
        this.RequestedQuantity = quantity;
      }
    
      public void ApproveToLowerOrEqualQuantity(int quantity) {
        if (quantity <= this.ApprovedQuantity)
        {
          // Requester type user can only update if lowering the approved quantity
          this.ApprovedQuantity = quantity;
        }
      }
    
      public void ApproveToHigherOrEqualtQuantity(int quantity) {
        if (quantity >= this.ApprovedQuantity)
        {
          // Supplier type user can only update if increasing the approved quantity
          this.ApprovedQuantity = quantity;
        }
      }
    }
    
    //Calling code
    public class ApplicationServiceOfSomeSort {
       public void RequestQuantity(UserId userId, OrderId orderId, int quantity) {
         var requester = requesterRepository.FromUser(userId);
         requester.MustBeAbleToRequestQuantity();
    
         var order = orderRepository.GetById(orderId);
         order.RequestQuantity(quantity);
       }
    
       public void ApproveQuantityAsRequester(UserId userId, OrderId orderId, int quantity) {
         var requester = requesterRepository.FromUser(userId);
         requester.MustBeAbleToApproveQuantity();
    
         var order = orderRepository.GetById(orderId);
         order.ApproveToLowerOrEqualQuantity(quantity);
       }
    
       public void ApproveQuantityAsSupplier(UserId userId, OrderId orderId, int quantity) {
         var supplier = supplierRepository.FromUser(userId);
         supplier.MustBeAbleToApproveQuantity();
    
         var order = orderRepository.GetById(orderId);
         order.ApproveToHigherOrEqualQuantity(quantity);
       }
    }
    

    当然,这个 API 周围仍有很多“难闻的气味”,但这是一个开始。

    【讨论】:

    • 感谢伊夫的反馈。 'requester.MustBeAbleToApproveQuantity()' 方法的目的是检查是否允许该操作并在不允许该操作时抛出异常或返回布尔结果?现在我在应用层有订单服务。服务器使用 IOrderAuthorizer 来确定用户所处的角色。然后我对已批准的数量进行更新。
    • 为了增加一点复杂性,如果有多个状态怎么办:草稿、提交和最终状态。在提交状态下,请求者可以以任何一种方式更新批准的数量,但在最终状态下,他们只能降低它(如简单示例)。这会改变您解决问题的方式吗?
    • MustXXX 方法在我的示例中抛出。
    • 这些状态,你的意思是这些状态是订单可以存在的吗?他们是否与供应商/请求者联系在一起,或者他们总是适用?无论如何,我觉得语言不通。看起来我们在谈论增加和减少数量,但称之为批准数量。这是为什么?为什么不明确地称其为增加和减少并传入要增加/减少的数量?从身份验证 POV 开始,身份验证将变为“MustBeAbleToIncrease/DecreaseQuantity”。状态将是 Order 内部的某种东西(即它会检查的不变量)。
    • 好问题。我对解释这个问题的任何背景知识不是很清楚。请求者可以从供应商处创建订单和数量(requestedQuantity)。供应商可以进去输入批准数量(approvalquantity)可以供应多少原始requestQuantity。提交/批准后,请求者和供应商都可以在特定方向编辑批准的数量。
    【解决方案2】:

    这受到 Yves 的回答和您的回复的启发。

    我个人的口头禅是将隐含的事情明确化,因为我喜欢应用此原则后代码的结果:

    public interface IProvideCurrentIdentityRoles
    {
       bool CanRequestQuantity()
       bool CanApproveQuantity();
       bool CanOverruleQuantityOnSubmittedOrder();
       bool CanIncreaseQuantityOnFinalOrder();
       bool CanDecreaseQuantityOnFinalOrder();
    }
    
    public class Order
    {
        public int OrderId {get; private set}
        public int RequestedQuantity {get; private set}
        public int ApprovedQuantity {get; private set}
    
        public void RequestQuantity(int quantity, IProvideCurrentIdentityRoles requester)
        {
            Guard.That(requester.CanRequestQuantity());
            this.RequestedQuantity = quantity;
        }
    
        public void ApproveQuantity(int quantity, IProvideCurrentIdentityRoles  approver)
        {
            if (quantity == this.RequestedQuantity)
            {
               Guard.That(approver.CanApproveQuantity());
            }
            else 
            {
               if (orderType == OrderType.Submitted)
               {
                  Guard.That(approver.CanOverruleQuantityOnSubmittedOrder());
               }
               else if (orderType == OrderType.Final)
               {
                  if (quantity > this.ApprovedQuantity)
                  {
                     Guard.That(approver.CanIncreaseQuantityOnFinalOrder());
                  }
                  else 
                  {
                     Guard.That(approver.CanDecreaseQuantityOnFinalOrder());
                  }
               }
            }
            this.ApprovedQuantity = quantity;
         }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-01-19
      • 1970-01-01
      • 2016-09-26
      • 2020-08-26
      • 1970-01-01
      • 2016-09-11
      • 1970-01-01
      • 2014-09-13
      • 2017-03-18
      相关资源
      最近更新 更多