Подход DDD, где применять бизнес-правила без агрегирования? - PullRequest
0 голосов
/ 03 января 2019

Новичок в DDD У меня есть простой случай, который я хотел бы смоделировать с использованием подхода DDD

2 объекта Student и Course

Соответствующим имуществом для студента являются StudentId и Budget

Соответствующими свойствами для Course являются CourseId и Price

Студент и курс - это сущности, которые могут существовать самостоятельно и иметь свой жизненный цикл

Бизнес-требования:

1) Студент может забронировать один курс (CourseId - это fk для таблицы Student)

2) Студент может забронировать курс, только если бюджет пользователя выше или равен цене курса.

3) Изменения цены курса не влияют на студентов, уже забронировавших курс.

4) Когда студент записывается на курс, его бюджет остается неизменным (возможно, изменится позже в конце курса)

5) Бюджет студента можно изменить, установив другую сумму, но новая сумма должна быть больше или равна цене курса, который заказал пользователь.
Установка меньшего значения должна вызвать ошибку времени выполнения.

Каким образом можно смоделировать этот простой случай, следуя доменному дизайну? Где применять правила двух бизнесов (пункты 2 и 5)?

Поскольку Курс может существовать без Студента, я не могу определить совокупность, где Студент является корневым объектом, а Курс его дочерним объектом. Можно я?

Но в то же время бизнес-правило, определенное в пункте 5, кажется мне инвариантом. Это так?

Так где и как применять эти правила?

Я попробовал сервисный подход, может работать для первого простого правила (пункт 2), но не работает для правила, описанного в пункте 5

var student = studentRepository.Get(srtudentId);
var course = courseRepository.Get(courseId)

var studentService = new StudentService();

studentService.SubScribeStudentToCourse(student, course);

studentRepository.Update(student);


StudentService.ChangeStudentBudget(student, 100000);

studentRepository.Update(student);  

когда я обновляю студента новым бюджетом, кто-то другой может изменить цену курса, делая бюджет студента непоследовательным

public class StudentService
{
    SubScribeStudentToCourse(Studen student, Course course)
    {
        if (studentt.Budget >= course.Price)
        {
            student.CourseId = course.CourseId
        }
    }

    ChangeStudentBudget( Student student, decimal budgetAmount)
    {
        if (student.CourseId != null)
        {
            var studentCourse = courseRepository.Get(student.CourseId);
            if ( studentCourse.Price <= budgetAmount)
            {
                student.Budget = budgetAmount;
            }
            else
            {
                throw new Exception("Budget should be higher than studentCourse.Price");
            }
        }
    }
}

Ответы [ 3 ]

0 голосов
/ 03 января 2019

Точка 5 - еще одно правило проверки. Но если цена курса может быть изменена, вам также нужно будет проверить это правило, а не только при изменении бюджета студента.

Это не инвариант. Инвариант касается только одного агрегата. У вас есть две совокупности.

Этот вопрос звучит для меня так, как будто я на него уже ответил.

0 голосов
/ 05 февраля 2019

Ваши агрегаты должны быть в конечном итоге непротиворечивыми, а не сильно, если только это действительно очень важный сценарий. Если это так, то рассмотрите возможность использования Saga или обновите их в одной транзакции. То, что вы должны сделать здесь, очень просто: StudentService.SubscribeTo () и CourceService.Enroll (). Это 2 метода должны происходить в 2 различных транзакциях. Во-первых, внутри StudentService.SubscribeTo вы получаете постоянство моделей учеников и курсов, затем вызываете student.SubscribeTo (курс). После операции вы вызываете Событие ученика assignToCourse для домена, и StudentDomainEventsHandler перехватывает его и вызывает CourceService.Enroll (), который получает модели ученика и курса из персистентности, а затем вызывает course.Enroll (student).

0 голосов
/ 03 января 2019

Гипотетические сценарии, как правило, сложно комментировать, но я добавлю свой ZAR0.02:)

У вас будет как Student , так и a Course совокупный корень. Если вам нужно отношение к другому определенному, сохраните список идентификаторов или объект-значение, представляющий другую сторону.

Для обеспечения соблюдения некоторых правил, которые не могут перекрываться, может быть проще иметь состояние относительно бюджета на Student. Например, если курс не находится в состоянии BudgetApproved, вы не можете добавить его к курсу. Чтобы изменить бюджет, сначала нужно изменить состояние, скажем, на «Бюджетирование». Таким образом, вы вводите более четкие шаги, которые позволяют лучше контролировать ваши инварианты.

Еще одно замечание об изменении цен. Эти вещи, вероятно, будут работать на основе «цитаты» в любом случае. После того как вы «примете» предложение, любые изменения в цене не будут иметь значения, за исключением случаев, когда есть «Ошибка» или «Упущение», которые могут и должны быть решены с использованием какого-либо бизнес-процесса или, если это не определено в системе, из-за группа. Order может быть Cancelled или «Заброшенный», а затем запускается какой-то другой процесс, например, возмещение.

...