我一直在学习 DDD,只是想确保我理解正确。我在不同的地方读到,聚合根属性不应直接引用其他聚合根中的实体。假设我们有 2 个聚合根:Student 和 Course。
最初,我会这样设计我的模型:
public class Student
{
private int Id {get;}
private readonly List<Course> _courses = new();
public IReadOnlyCollection<Course> Courses => _courses;
public void Enroll(Course course)
{
_courses.Add(course);
}
}
public class Course
{
private int Id {get;}
private readonly List<Student> _students = new();
public IReadOnlyCollection<Student> Students => _students;
public void AddStudent(Student student)
{
_students.Add(student);
}
}
在了解了不引用其他聚合中的实体后,我最终得到了 id 集合:
public class Student
{
private int Id {get;}
private readonly List<int> _courses = new();
public IReadOnlyCollection<int> Courses => _courses;
public void Enroll(Course course)
{
_courses.Add(course.Id);
}
}
这是正确的吗?感觉有点奇怪而且违反直觉。我也不喜欢现在没有类型保证我的集合元素将是预期的类型。我可能会错误地更改代码来执行
_students.Add(course.Id)
操作,并且该错误很难发现。有什么建议可以避免这种情况吗?
我也不喜欢现在没有类型保证我的集合元素将是预期的类型。
这里的标准答案:将实体标识符本身建模为类型 - 又名“值对象”。
public class StudentId {
// ...
}
public class Student {
StudentId id;
// ...
}
TADA - 你又拥有类型安全了。作为额外的好处,值对象还减少了直接耦合到内存表示的代码量,从而减少了您以后发现标识符应该是字符串时需要执行的工作量。
请参阅领域驱动设计(Evans,2003)第 5 章。
正确吗?
也许?
通常情况下,“关系集合”将被建模为标识符集合,期望您在创建报告时可以将所有内容连接在一起。
这是否意味着我们应该在 Student 中拥有课程标识符的集合?或者课程中学生标识符的集合?或者是其他东西?上述任何一种都是可能的,具体取决于您试图满足的需求。