IT TIP

도메인 중심 설계의 검증

itqueen 2020. 12. 4. 21:39
반응형

도메인 중심 설계의 검증


도메인 기반 설계에서 복잡한 집계에 대한 유효성 검사를 어떻게 처리합니까? 비즈니스 규칙 / 검증 로직을 통합합니까?

인수 검증을 이해합니다. 그리고 모델 자체에 첨부 할 수있는 속성 유효성 검사를 이해하고 이메일 주소 나 우편 번호가 유효한지 또는 이름의 최소 및 최대 길이를 확인하는 등의 작업을 수행합니다.

하지만 여러 모델을 포함하는 복잡한 검증은 어떻습니까? 일반적으로 아키텍처 내에서 이러한 규칙과 방법을 어디에 배치합니까? 그리고이를 구현하기 위해 어떤 패턴을 사용합니까?


이 문제에 대한 Jimmy Bogard의 솔루션이 마음에 듭니다. 그는 자신의 블로그에 "방문자 및 확장 메서드를 사용한 엔티티 유효성 검사" 라는 제목의 게시물을 가지고 있으며 여기에서 유효성 검사 코드를 저장하기위한 별도의 클래스 구현을 제안하는 엔티티 유효성 검사에 대한 매우 우아한 접근 방식을 제시합니다.

public interface IValidator<T>
{
    bool IsValid(T entity);
    IEnumerable<string> BrokenRules(T entity);
}

public class OrderPersistenceValidator : IValidator<Order>
{
    public bool IsValid(Order entity)
    {
        return BrokenRules(entity).Count() == 0;
    }

    public IEnumerable<string> BrokenRules(Order entity)
    {
        if (entity.Id < 0)
            yield return "Id cannot be less than 0.";

        if (string.IsNullOrEmpty(entity.Customer))
            yield return "Must include a customer.";

        yield break;
    }
}

IsValid(xx)애플리케이션 전체 에서 호출 에 의존하는 대신 Greg Young의 조언을 받아보십시오.

엔티티가 잘못된 상태가되지 않도록하십시오.

이것이 기본적으로 의미하는 것은 엔티티를 순수한 데이터 컨테이너로 생각하고 동작이있는 객체에 대해 더 많이 생각하는 것에서 전환한다는 것입니다.

개인 주소의 예를 고려하십시오.

 person.Address = "123 my street";
 person.City = "Houston";
 person.State = "TX";
 person.Zip = 12345;

이러한 호출 사이에 엔티티가 유효하지 않습니다 (서로 동의하지 않는 속성이 있기 때문입니다. 이제 다음을 고려하십시오.

person.ChangeAddress(.......); 

주소 변경 동작과 관련된 모든 호출은 이제 원자 단위입니다. 귀하의 법인은 여기서 유효하지 않습니다.

상태가 아니라 동작을 모델링하는이 아이디어를 취하면 잘못된 엔티티를 허용하지 않는 모델에 도달 할 수 있습니다.

이에 대한 좋은 토론을 보려면 다음 infoq 인터뷰를 확인하십시오. http://www.infoq.com/interviews/greg-young-ddd


저는 보통 사양 클래스를 사용하며 메서드를 제공합니다 (이것은 C #이지만 모든 언어로 번역 할 수 있습니다).

bool IsVerifiedBy(TEntity candidate)

이 방법은 후보와 그 관계에 대한 완전한 검사를 수행합니다. 사양 클래스의 인수를 사용하여 검사 수준과 같이 매개 변수화 할 수 있습니다.

후보자가 사양을 확인하지 않은 이유를 알 수있는 방법을 추가 할 수도 있습니다.

IEnumerable<string> BrokenRules(TEntity canditate) 

다음과 같이 첫 번째 방법을 구현하기로 결정할 수 있습니다.

bool IsVerifiedBy(TEntity candidate)
{
  return BrokenRules(candidate).IsEmpty();
}

위반 규칙의 경우 일반적으로 반복자를 작성합니다.

IEnumerable<string> BrokenRules(TEntity candidate)
{
  if (someComplexCondition)
      yield return "Message describing cleary what is wrong...";
  if (someOtherCondition) 
      yield return
   string.Format("The amount should not be {0} when the state is {1}",
        amount, state);
}

지역화의 경우 리소스를 사용해야하며 BrokenRules 메서드에 문화권을 전달하면 안됩니다. 이 클래스를 사용을 제안하는 이름으로 모델 네임 스페이스에 배치합니다.


다중 모델 유효성 검사는 집계 루트를 거쳐야합니다. 집계 루트에서 유효성을 검사해야하는 경우 디자인 결함이있을 수 있습니다.

집계에 대한 유효성 검사를 수행하는 방법은 유효성 검사 통과 / 실패 여부와 실패 이유에 대한 메시지를 알려주는 응답 인터페이스를 반환하는 것입니다.

집계 루트에서 모든 하위 모델의 유효성을 검사하여 일관성을 유지할 수 있습니다.

// Command Response class to return from public methods that change your model
public interface ICommandResponse
{
    CommandResult Result { get; }
    IEnumerable<string> Messages { get; }
}

// The result options
public enum CommandResult
{
    Success = 0,
    Fail = 1
}

// My default implementation
public class CommandResponse : ICommandResponse
{
    public CommandResponse(CommandResult result)
    {
        Result = result;
    }

    public CommandResponse(CommandResult result, params string[] messages) : this(result)
    {
        Messages = messages;
    }

    public CommandResponse(CommandResult result, IEnumerable<string> messages) : this(result)
    {
        Messages = messages;
    }

    public CommandResult Result { get; private set; }

    public IEnumerable<string> Messages { get; private set; }
}

// usage
public class SomeAggregateRoot
{
    public string SomeProperty { get; private set; }


    public ICommandResponse ChangeSomeProperty(string newProperty)
    {
        if(newProperty == null)
        {
            return new CommandResponse(CommandResult.Fail, "Some property cannot be changed to null");
        }

        SomeProperty = newProperty;

        return new CommandResponse(CommandResult.Success);
    }
}

이 질문은 이제 조금 오래되었지만 누군가가 관심이있는 경우 여기에 내 서비스 클래스에서 유효성 검사를 구현하는 방법이 있습니다.

I have a private Validate method in each of my service classes that takes an entity instance and action being performed, if validation fails a custom exception is thrown with the details of the broken rules.

Example DocumentService with built in validation

public class DocumentService : IDocumentService
{
    private IRepository<Document> _documentRepository;

    public DocumentService(IRepository<Document> documentRepository)
    {
        _documentRepository = documentRepository;
    }

    public void Create(Document document)
    {
        Validate(document, Action.Create);

        document.CreatedDate = DateTime.Now;

        _documentRepository.Create(document);
    }

    public void Update(Document document)
    {
        Validate(document, Action.Update);

        _documentRepository.Update(document);
    }

    public void Delete(int id)
    {
        Validate(_documentRepository.GetById(id), Action.Delete);

        _documentRepository.Delete(id);
    }

    public IList<Document> GetAll()
    {
        return _documentRepository
            .GetAll()
            .OrderByDescending(x => x.PublishDate)
            .ToList();
    }

    public int GetAllCount()
    {
        return _documentRepository
            .GetAll()
            .Count();
    }

    public Document GetById(int id)
    {
        return _documentRepository.GetById(id);
    }

    // validation 

    private void Validate(Document document, Action action)
    {
        var brokenRules = new List<string>();

        if (action == Action.Create || action == Action.Update)
        {
            if (string.IsNullOrWhiteSpace(document.Title))
                brokenRules.Add("Title is required");

            if (document.PublishDate == null)
                brokenRules.Add("Publish Date is required");
        }

        if (brokenRules.Any())
            throw new EntityException(string.Join("\r\n", brokenRules));
    }

    private enum Action
    {
        Create,
        Update,
        Delete
    }
}

I like this approach because it allows me to put all my core validation logic in one place which keeps things simple.

참고URL : https://stackoverflow.com/questions/516615/validation-in-a-domain-driven-design

반응형