2

우리 응용 프로그램에서는 비즈니스 규칙과 현재 사용자의 컨텍스트를 기반으로 속성 업데이트의 유효성을 검사해야하는 시나리오가 있습니다. 도메인 모델이 현재 사용자에 대해 알지 않아야한다고 생각하기 때문에 유효성 검사를 수행하는 가장 좋은 방법을 결정하려고합니다. Google의 정상적인 승인은 도메인과 별개이며이 시나리오와 다릅니다.컨텍스트 도메인 기반 모델 유효성 검사

이 유효성 검사는 어디에서 발생해야하며이를 처리하는 더 좋은 방법이 있습니까? 도메인 모델이 사용자에 대해 알고 있어야합니까? 어떤 도움이나 의견을 주시면 감사하겠습니다.

간단한 예 : 승인 수량이있는 주문이 있습니다. 특정 사용자 유형 만 수량을 특정 방향으로 업데이트 할 수 있습니다. 이것이 도메인 집계에서 유효성을 검사하는 올바른 방법입니까?

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

답변

2

이블은 이브의 답변과 그에 대한 답장으로 약간의 영향을받습니다. 피드백 이브에 대한

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

이 enum-like 유형 (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를 주변의 많은 여전히하지만 시작입니다.

+0

감사 : 나는 코드가이 원리를 적용한 후 밝혀 방법을 원하는대로

내 개인 만트라는 암시 일을 명시 적으로 확인하는 것입니다. 'requester.MustBeAbleToApproveQuantity()'메소드는 액션이 ​​허용되는지 검사하고 예외가 발생하거나 액션이 허용되지 않으면 부울 결과를 반환합니까? 지금은 애플리케이션 계층에 주문 서비스가 있습니다. 서버는 IOrderAuthorizer를 사용하여 사용자의 역할을 결정합니다. 그러면 승인 된 수의 업데이트를 수행합니다. – user2354863

+0

좀 더 복잡해지기 위해 초안, 제출 됨 및 최종 상태 등 여러 상태가있는 경우 어떻게됩니까? 제출 된 상태에서 요청자는 승인 된 수량을 어느 쪽이든 업데이트 할 수 있지만 최종 상태에서는 단순한 예와 같이 만 낮출 수 있습니다. 이것이 당신이 문제에 접근하는 방법을 바꿀까요? – user2354863

+0

MustXXX 메소드가 예제에서 던졌습니다. –