문제

나는 빈혈 도메인이 실제로 무엇을 의미하는지에 대한 명확하고 간단한 예를 찾으려고 노력했습니다.주변에는 많은 이론이 있고, 잘 대답된 질문도 많이 있습니다.그럼에도 불구하고 나는 "빈혈 영역"의 의미가 실제로 어느 정도까지 진행되는지에 대해 명확한 그림을 얻을 수 없었습니다.따라서 빈약한 도메인 설계의 실제적인 더미 예제를 보고 이것이 어떻게 도메인 기반 설계로 발전할 수 있는지 묻는 것보다 더 간단할 것이라고 믿습니다.

따라서 다음 유형의 데이터 엔터티가 있다고 가정해 보겠습니다. 태스크데이터:

public class TaskData
{
    public Guid InternalId { get; set; }

    public string Title { get; set; }

    public string Details { get; set; }

    public TaskState ExplicitState { get; set; }

    public IEnumerable<TaskData> InnerTasks { get; set; }

}

그리고 "라는 추가 속성이 필요합니다.실제상태"는 계산된 상태입니다.Task에 내부 하위 작업이 있는 경우 값은 엄격하게 하위 작업에 따라 달라집니다. 그렇지 않으면 "실제상태" 동일하다 "명시적 상태"

이 논리를 별도로 작성하면 서비스 수업 (저는 그들을 "라고 부릅니다.엔진") 우리는 다음을 가지고 있습니다 :

internal class TaskStateCalculator
{
    public TaskState GetState(TaskData taskData)
    {
        if (taskData.InnerTasks.Any())
        {
            if (taskData.InnerTasks.All(x => this.GetState(x) == TaskState.Done))
            {
                return TaskState.Done;
            }
            if (taskData.InnerTasks.Any(x => this.GetState(x) == TaskState.InProgress))
            {
                return TaskState.InProgress;
            }

            return TaskState.Default;
        }

        return taskData.ExplicitState;
    }       
}

그만큼 첫 번째 질문 이다:

위의 코드는 빈약한 도메인 설계를 반영합니까? TaskState계산기 서비스/엔진은 내 도메인 계층의 일부입니까?그렇다면 이를 방지하려면 논리를 내부로 이동해야 합니다. 태스크데이터 클래스(그리고 이름 바꾸기 태스크데이터 에게 ).내가 맞나요?

그만큼 두 번째 질문 (실제로는 체인입니다):

만약 우리가 더 어려운 상황에 처해 있다면 어떨까요?다음과 같은 속성이 필요하다고 가정해 보겠습니다. ComputeSomething 내부에 엔터티이며 이 속성의 논리는 전체 작업에 액세스해야 합니다. 저장소.이 경우, 클래스는 다음에 의존합니다. 작업 저장소.괜찮을까요?EF는 그러한 클래스의 인스턴스를 어떻게 구성합니까?대안은 무엇입니까?

도움이 되었습니까?

해결책

나는 빈혈 도메인이 실제로 무엇을 의미하는지에 대한 명확하고 간단한 예를 찾으려고 노력했습니다.

사실 빈약한 도메인 모델에서 풍부한 도메인 모델로 전환하는 것은 정말 쉽습니다.

  1. 모든 속성 설정자를 다음으로 설정합니다. private 모델의 상태를 변경하려면 메서드를 추가하세요.
  2. 모두 평가 데메테르의 법칙 위반 사항을 확인하고 적절한 곳에 메소드를 추가하세요.

결국 올바른 모델을 갖게 될 것입니다.

귀하의 경우에는 그 논리를 내부에 캡슐화하겠습니다. TaskData TaskStateCalculator가 디미터 법칙을 위반하므로

public class TaskData
{
    public Guid InternalId { get; private set; }

    public string Title { get; private set; }

    public string Details { get; private set; }

    public TaskState ExplicitState { get; private set; }

    public IEnumerable<TaskData> InnerTasks { get; private set; }

    public TaskState GetState()
    {
        if (!InnerTasks.Any())
            return ExplicitState;

        if (InnerTasks.All(x => this.GetState(x) == TaskState.Done))
        {
            return TaskState.Done;
        }

        if (InnerTasks.Any(x => this.GetState(x) == TaskState.InProgress))
        {
            return TaskState.InProgress;
        }

        return TaskState.Default;
    }       
}

또 다른 점은 InnerTasks 컬렉션을 외부 세계에 전혀 노출하지 않을 것이라는 점입니다(단지 멤버 필드로만 사용).하지만 다른 시나리오에서는 클래스가 어떻게 사용되는지 모르기 때문에 말하기가 어렵습니다.

개인 설정자가 필요한 이유

둘 이상의 속성을 변경해야 할 때마다 메서드를 사용하여 동작을 설명하는 것이 더 나은 경우가 많습니다. 그러면 필요한 모든 속성을 변경하는 것을 잊어버릴 수 없기 때문입니다.또한 메서드는 속성 집합을 변경하는 것보다 수행하려는 작업을 더 잘 설명합니다.

단일 속성만 변경하더라도 변경 사항이 클래스의 나머지 정보와 호환되지 않을 수 있으므로 해당 속성은 클래스를 잘못된 상태로 설정할 수 있습니다.캡슐화는 OOP의 핵심 원칙 중 하나라는 것을 잊지 마세요.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top