DDD的新手我有一个简单的案例,我想使用DDD方法进行建模
2个实体Student
和Course
学生的相关属性是StudentId和预算
课程的相关属性是CourseId和Price
学生和课程是可以独立存在并具有自己生命周期的实体
业务要求:
1)学生可以预订一门课程(“学生表”的课程编号是fk)
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");
}
}
}
}
答案 0 :(得分:2)
假设情景通常很难评论,但我将添加ZAR0.02:)
您将同时拥有Student
和 Course
聚合根。如果需要与定义的另一方的关系,则存储ID列表或代表另一方的值对象。
要强制执行某些不能重叠的规则,可以更简单地说明Student
上的预算状态。例如,如果课程未处于BudgetApproved
状态,则无法添加到课程。为了更改预算,您首先需要将状态更改为“预算”。这样,您将引入更多不同的步骤,从而可以更好地控制不变量。
关于改变价格的另一个说明。无论如何,这些事情可能会在“引用”的基础上起作用。一旦“接受”报价,价格的任何变化都是不相关的,除非存在“错误”或“遗漏”,这些错误或“遗漏”可以并且应该使用某些业务流程来处理,或者,如果未在系统中定义,则超出带。 Order
可能是Cancelled
或“被遗弃”,然后启动了其他程序,例如报销。
答案 1 :(得分:1)
第5点是另一个验证规则。但是,如果可以修改课程价格,则不仅要修改学生预算,还必须在那里检查规则。
它不是不变的。不变只涉及一个集合。您有两个聚合。
这个问题听起来像我已经回答了。
答案 2 :(得分:0)
除非是一个非常重要的情况,否则您的汇总最终必须是一致的,而不是强一致性。如果是这样,则考虑使用Saga,或在一项交易中对其进行更新。您在这里应该做的很简单:StudentService.SubscribeTo()和CourceService.Enroll()。这2种方法应在2个不同的事务中发生。首先,在StudentService.SubscribeTo内部,从持久性中获取学生和课程模型,然后调用student.SubscribeTo(course)。操作完成后,您引发学生assignedToCourse域事件,然后StudentDomainEventsHandler捕获该事件,并调用CourceService.Enroll()从持久性中获取学生和课程模型,然后调用course.Enroll(student)。