다음을 통해 공유


도메인 모델 계층의 디자인 유효성 검사

팁 (조언)

이 콘텐츠는 .NET Docs 또는 오프라인으로 읽을 수 있는 다운로드 가능한 무료 PDF로 제공되는 컨테이너화된 .NET 애플리케이션용 .NET 마이크로 서비스 아키텍처인 eBook에서 발췌한 내용입니다.

컨테이너화된 .NET 애플리케이션을 위한 .NET 마이크로서비스 아키텍처 eBook의 표지 썸네일.

DDD에서 유효성 검사 규칙은 고정으로 간주될 수 있습니다. 집계의 주요 책임은 해당 집계 내의 모든 엔터티에 대해 상태 변경 내용 간에 고정을 적용하는 것입니다.

도메인 엔터티는 항상 유효한 엔터티여야 합니다. 특정 객체에 대해서 항상 참이어야 하는 불변 조건들이 있습니다. 예를 들어 주문 항목 개체에는 항상 양의 정수여야 하는 수량과 아티클 이름 및 가격이 있어야 합니다. 따라서 고정 적용은 도메인 엔터티(특히 집계 루트)의 책임이며 엔터티 개체는 유효하지 않고 존재할 수 없어야 합니다. 고정 규칙은 단순히 계약으로 표현되며 위반 시 예외 또는 알림이 발생합니다.

그 이유는 개체가 절대 있어서는 안 되는 상태에 있기 때문에 많은 버그가 발생한다는 것입니다.

이제 UserProfile을 사용하는 SendUserCreationEmailService가 있음을 제안해 보겠습니다. 해당 서비스에서 Name이 null이 아닌 경우 어떻게 합리화할 수 있나요? 다시 확인합니까? 더 가능성이 높은 것은 ... 당신이 확인하는 것을 귀찮아하고 "잘 되기를 바라며" 그냥 넘어가는 것입니다 - 다른 사람이 그것을 유효성을 검사했을 것이라고 바라면서 말이죠. 물론 TDD를 사용하여 작성해야 하는 첫 번째 테스트 중 하나는 null 이름을 가진 고객을 보내면 오류가 발생한다는 것입니다. 그러나 일단 우리가 이런 종류의 테스트를 반복해서 쓰기 시작하면 우리는 깨닫습니다 ... "이름이 null이 되는 것을 결코 허용하지 않는다면 어떨까요? 이러한 모든 테스트는 없을 것입니다!".

도메인 모델 계층에서 유효성 검사 구현

유효성 검사는 일반적으로 도메인 엔터티 생성자 또는 엔터티를 업데이트할 수 있는 메서드에서 구현됩니다. 유효성 검사에 실패할 경우 데이터 확인 및 예외 발생과 같은 여러 가지 방법으로 유효성 검사를 구현할 수 있습니다. 유효성 검사에 사양 패턴을 사용하는 것과 같은 고급 패턴과 각 유효성 검사에 대한 예외를 반환하는 대신 오류 컬렉션을 반환하는 알림 패턴도 있습니다.

조건의 유효성을 검사하고 예외를 발생시킵니다.

다음 코드 예제에서는 예외를 발생시켜 도메인 엔터티의 유효성 검사에 대한 가장 간단한 방법을 보여 줍니다. 이 섹션의 끝에 있는 참조 테이블에서 앞에서 설명한 패턴에 따라 고급 구현에 대한 링크를 볼 수 있습니다.

public void SetAddress(Address address)
{
    _shippingAddress = address?? throw new ArgumentNullException(nameof(address));
}

더 나은 예는 내부 상태가 변경되지 않았는지 또는 메서드에 대한 모든 돌연변이가 발생했는지 확인해야 하는 필요성을 보여 줍니다. 예를 들어 다음 구현은 개체를 잘못된 상태로 둡니다.

public void SetAddress(string line1, string line2,
    string city, string state, int zip)
{
    _shippingAddress.line1 = line1 ?? throw new ...
    _shippingAddress.line2 = line2;
    _shippingAddress.city = city ?? throw new ...
    _shippingAddress.state = (IsValid(state) ? state : throw new …);
}

주가 유효하지 않은 경우 첫 번째 주소 줄과 구/군/시가 이미 변경되었습니다. 주소가 유효하지 않을 수 있습니다.

엔터티의 생성자에서 유사한 방법을 사용하여 엔터티가 만들어지면 유효한지 확인하기 위해 예외를 발생시킬 수 있습니다.

데이터 주석을 기반으로 모델에서 유효성 검사 특성 사용

필수 또는 MaxLength 특성과 같은 데이터 주석은 테이블 매핑 섹션에 자세히 설명된 대로 EF Core 데이터베이스 필드 속성을 구성하는 데 사용할 수 있지만. .NET Framework의 EF 4.x 이후와 같이 EF Core의 엔터티 유효성 검사에는 더 이상 작동하지 않습니다(메서드도 아닙니다 IValidatableObject.Validate ).

데이터 주석 및 IValidatableObject 인터페이스는 평소와 같이 컨트롤러의 작업을 호출하기 전에 모델 바인딩 중에 모델 유효성 검사에 계속 사용할 수 있지만 해당 모델은 ViewModel 또는 DTO이며 도메인 모델 문제가 아닌 MVC 또는 API 문제입니다.

개념적 차이를 명확히 했기 때문에 작업에서 권장되지 않는 엔터티 클래스 개체 매개 변수를 수신하는 경우 유효성 검사를 위해 데이터 주석과 IValidatableObject 엔터티 클래스를 계속 사용할 수 있습니다. 이 경우 작업을 호출하기 직전에 모델 바인딩 시 유효성 검사가 수행되고 컨트롤러의 ModelState.IsValid 속성을 확인하여 결과를 확인할 수 있지만, EF 4.x 이후와 마찬가지로 DbContext에서 엔터티 개체를 유지하기 전이 아니라 컨트롤러에서 다시 수행됩니다.

DbContext의 SaveChanges 메서드를 재정의하여 데이터 주석 및 IValidatableObject.Validate 메서드를 사용하여 엔터티 클래스에서 사용자 지정 유효성 검사를 구현할 수 있습니다.

IValidatableObject에서 엔터티의 유효성을 검사하기 위한 샘플 구현을 볼 수 있습니다. 이 샘플은 특성 기반 유효성 검사를 수행하지 않지만 동일한 재정의에서 리플렉션을 사용하여 쉽게 구현할 수 있어야 합니다.

그러나 DDD 관점에서 도메인 모델은 엔터티의 동작 메서드에서 예외를 사용하거나 유효성 검사 규칙을 적용하기 위해 사양 및 알림 패턴을 구현하는 것이 가장 좋습니다.

입력을 허용하는 ViewModel 클래스(도메인 엔터티 대신)의 애플리케이션 계층에서 데이터 주석을 사용하여 UI 계층 내에서 모델 유효성 검사를 허용하는 것이 좋습니다. 그러나 도메인 모델 내에서 유효성 검사를 제외할 때는 이 작업을 수행해서는 안 됩니다.

사양 패턴 및 알림 패턴을 구현하여 엔터티 유효성 검사

마지막으로, 도메인 모델에서 유효성 검사를 구현하는 보다 정교한 방법은 나중에 나열된 일부 추가 리소스에 설명된 대로 알림 패턴과 함께 사양 패턴을 구현하는 것입니다.

이러한 패턴 중 하나만 사용할 수도 있습니다. 예를 들어 컨트롤 문을 사용하여 수동으로 유효성을 검사하지만 알림 패턴을 사용하여 유효성 검사 오류 목록을 쌓고 반환할 수도 있습니다.

도메인에서 지연된 유효성 검사 사용

도메인에서 지연된 유효성 검사를 처리하는 다양한 방법이 있습니다. 본 버논은 Domain-Driven 디자인 구현 저서에서 유효성 검사 섹션에서 이에 대해 설명합니다.

2단계 유효성 검사

또한 2단계 유효성 검사를 고려합니다. DDO(데이터 전송 개체) 명령에서 필드 수준 유효성 검사와 엔터티 내의 도메인 수준 유효성 검사를 사용합니다. 유효성 검사 오류를 보다 쉽게 처리할 수 있도록 예외 대신 결과 개체를 반환하여 이 작업을 수행할 수 있습니다.

예를 들어 데이터 주석과 함께 필드 유효성 검사를 사용하면 유효성 검사 정의를 복제하지 않습니다. 하지만 DDO의 경우 실행은 서버 쪽과 클라이언트 쪽 모두일 수 있습니다(예: 명령 및 ViewModels).

추가 리소스