对于DDD,我有一个简单的案例,我想使用DDD方法建模
2实体-学生和课程
学生的相关财产是StudentId和预算
当然,相关的属性是CourseId和价格
学生和课程是可以独立存在并有自己生命周期的实体。
1)学生可以预定一门课程(CourseId是表示学生表的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");
}
}
}
}发布于 2019-01-03 09:18:48
这两种业务规则都是对“学生”实体的限制。这意味着它们可以由Student实体本身强制执行,也可以由与您一样的服务类强制执行。
关于预算变更的规则的编写方式
5)学生预算可以修改,设置不同的金额,但新的金额必须高于或等于用户预定的课程的价格。设置较低的金额会引发运行时错误。
没有说明应该考虑哪个课程的价格(如果在预订课程和改变预算之间的价格发生了变化)。这是你应该要求澄清的事情。
如果新的预算必须足以支付当前的课程价格,那么您当前的实现几乎是好的(您错过了在预定课程之前更改预算的情况)。
如果新的预算必须包括在预订时的课程价格,你将不得不记住这个数额。这可以是在“学生”实体中,也可以是“课程实体”保存了随时间变化的价格记录,“学生实体”记录了课程预订后的情况。
发布于 2019-01-03 19:53:52
过度使用服务是贫血模式的一个明显标志。您的系统可以非常简单地建模:
class Student
{
private studentId;
private budget;
private registration;
Book( Registration registration )
{
if( registration.Price > this.budget )
throw;
this.registration = registration;
}
ChangeBudget( amount )
{
if( this.registration && this.registration.Price > amount )
throw;
this.budget = amount;
}
}
class Course
{
private courseId;
private price;
// Factory Method
OpenRegistration( offers )
{
price = ; // do something to calculate new price
return new Registration( this.courseId, price );
}
}
// Value Object
class Registration
{
public CourseId { get; }
public Price { get; }
Registration( courseId, price )
{
CourseId = courseId;
Price = price;
}
}然后在你的BookCourseCommandHandler中:
student = students.Find( cmd.StudentId );
course = courses.Find( cmd.CourseId );
registration = course.OpenRegistration( cmd.SpecialOffers );
student.Book( registration );
students.Save( student );这里的关键是上面的内容是声明性的。没有“询问”数据,所以很容易看到正在发生的事情。
https://softwareengineering.stackexchange.com/questions/384878
复制相似问题