Search code examples
c#validationdomain-driven-designdomain-modelapplication-layer

Contextual Domain Driven Model Validation


In our application we have a scenario where we need to validate an property update based on business rules and the context of the current user. I am trying to determine the best way to do the validation because I think that the domain model should not know about the current user. Our normal authorization is separate from the domain and is different than this scenario.

Where should this validation occur and is there a better way to handle it? Should the domain model know about the user? Any help or input is appreciated.

Simple Example: We have an order with an approved quantity. Only specific user types can update the quantity in only specific directions. Is this the correct way to validate in a domain aggregate?

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;
            }
        }
    }
}

Solution

  • This is slightly inspired by Yves answer and your replies to it.

    My personal mantra is to make implicit things explicit, as I like the way the code turns out after applying this principle:

    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;
         }
    }