Domain driven design 处理聚合根
我是DDD的新手,所以我正在做一些练习来了解更多一点。我有以下规则的课程BC: 必须先创建课程,然后他们才能创建一门课程的模块 每个模块将由用户在上传作业时完成 当用户完成所有模块时,课程将由用户完成 定义: 课程涵盖特定主题,由模块组成。例如,sap课程有10个模块,如:模块1:it是什么?模块2:如何使用it 在此之后,我意识到课程是模块的聚合根,因为模块已完成,我必须关闭课程的用户状态 该模式将是:Domain driven design 处理聚合根,domain-driven-design,aggregateroot,Domain Driven Design,Aggregateroot,我是DDD的新手,所以我正在做一些练习来了解更多一点。我有以下规则的课程BC: 必须先创建课程,然后他们才能创建一门课程的模块 每个模块将由用户在上传作业时完成 当用户完成所有模块时,课程将由用户完成 定义: 课程涵盖特定主题,由模块组成。例如,sap课程有10个模块,如:模块1:it是什么?模块2:如何使用it 在此之后,我意识到课程是模块的聚合根,因为模块已完成,我必须关闭课程的用户状态 该模式将是: public class Course : AggregateRoot { pri
public class Course : AggregateRoot
{
private string title;
private List<Module> modules;
}
但模块也是作业的聚合根,因为当用户上传作业时,模块必须关闭。这使我认为这种方法是错误的,因为在DDD中不可能有嵌套的聚合根。有人知道怎么了
[更新]
好了,现在我明白了工作是如何进行的,以及为什么你把它分成了公元前2年。然而,我做了一些改变,一些问题浮现在我的脑海中
-我已将enroll方法创建为静态,并将构造函数设置为私有
-课程必须是一个数组,因为一个学生可以有多个
-我已经设置了更多与课程和老师相关的参数。当然是老师和实体,对吗
-我创建status of course是为了在模块完成时更新它,这样我就不必阅读所有模块就能知道它。行吗
-如何传递每个模块的更多信息,如标题和说明?课程实体是如何创建所有模块的,对吗
public class StudentEnrolment: AggregateRoot
{
private StudentId studentId;
private Course courses;
private constructor(
StudentId studentId,
Course course,
){
this.studentId= studentId;
this.courses[] = course;
}
public statuc function enroll(
StudentId studentId,
CourseId courseId,
string courseTitle,
string courseLink,
string teacherId,
string teacherName,
List<Tuple<ModuleId, string>> modules) {
teacher = new Teacher(...);
courseStatus = new courseStatus();
new course(courseTitle, courseLink, courseStatus, teacher);
return new self(studentId, course);
}
public function void uploadModuleHomework(ModuleId moduleId, Homework homework){
/* forward to course.uploadModuleHomework */
}
public boolean isCourseFinished(){
/* forward to course.isFinished */
}
public List<Tuple<ModuleId, string>> getModules(){
/* forward to course.getModules */
}
}
有两个不同的子域,因此我们有两个有界上下文: 1.课程和模块管理,教师可以管理这些课程和模块;在这里,课程和模块可以是聚合根,课程可以包含对模块ID的引用,而不是对实例的引用
public class Course: AggregateRoot
{
private string title;
private List<ModuleId> modules;
}
如果需要查询注册以获取家庭作业,则不应返回家庭作业列表,因为客户端代码会认为它可以直接调用家庭作业.uploadfile,这是不允许的,只有聚合根可以修改其内部实体。相反,您可以返回元组或更好的方法,您可以创建家庭作业类的不可变版本。您不能有嵌套的AR,但可以在AR中有嵌套的实体。好的,您的意思是模块将是一个实体,该实体具有另一个称为家庭作业的实体。但是你怎么看待这种方法呢?你会用不同的方式吗?我会问业务专家一致性要求是什么。我已经解释过了。在上面的文本中有3条规则。当一个新模块被添加到课程中时会发生什么?@AgustinCastro在我的回答中,StudentenRolliment只适用于一门课程,它可以被重命名为StudentenRollimentToCourse。不需要AR建模学员的所有课程注册,也不需要商业不变量protect@AgustinCastro我修改了我的答案以更好地模拟你的案例。我不明白两件事:第一,为什么你认为家庭作业比模块更重要?对我来说,作业必须在模块内。因为我要向学生展示所有课程,每门课程都有模块,每个模块都有家庭作业。这是自然的流动,对吗?第二个问题:如果课程和模块都有ID,为什么它们是VO而不是实体。我在想的一件事是,保存每个实体课程、模块的ID并不是更好。。。不是保存标题,而是保存描述。然后我们需要额外的信息,我可以点击其他BC课程和模块admin@AgustinCastro1.在这个有界上下文BC2中,模块和过程是值对象。在BC1中,它们是聚合体。这不是一个重要的问题,而是一个分离的问题。一个BC中的聚合是其他BC中的值对象。在BC2中,只需要他们的ID和头衔,而且它们也是不可变的。@AgustinCastro 2。他们有身份证是无关紧要的;需要该ID来链接到BC1中的聚合;如果你不需要它,你可以删除它,但我认为你会这样做,即在UI中显示某种链接。标题是为了不去BC1并得到它,同样,是为了更好地分离BCs。正如您所说,您可以放弃它,但这样,如果BC1(即DB)下降,您将有更好的恢复能力,它也不会使BC2下降。
public class StudentEnrolment: AggregateRoot
{
private StudentId studentId;
private Course course;
private List<Homework> homeworks;
// initialize a student enrolment as public constructor or make constructor private and use a static method
// here is important to notice that only this AR creates its entities, it does not receive them as parameter
public constructor(
StudentId studentId,
Course course,
List<Module> modules){
this.studentId = studentId;
this.course = course;
//build the the homeworks entity list based on the modules parameter
//for each module create a Homework entity, that initially is not uploaded, like:
this.homeworks = modules.map(module => new Homework(module))
}
public function void uploadFileForHomework(ModuleId moduleId, string file){
/* find the homework by module Id and upload file*/
}
public boolean isCourseFinished(){
/*returns true if all homeworks are uploaded*/
/*optimization: you could have a status that is updated when a homework's file is uploaded*/
}
public List<Tuple<ModuleId, string, boolean>> getHomeworks(){
/* returns a list of readonly Homeworks, i.e. Tuple<ModuleId, string /*module title*/, boolean /*is uploaded*/> */
}
}
public class Homework: Entity
{
private Module module;
private string file;
public constructor(Module module){
this.module = module;
}
public void upload(string file){ this.file = file;}
public boolean isUploaded(){return (boolean)this.file;}
public string getUploadedFile(){return this.file;}
public ModuleId getModuleId(){return this.module.getId();}
}
public class Course: ValueObject
{
private string title;
private CourseId id;
public constructor(id, title){...}
public string getTitle(){return this.title;}
public string getId(){return this.title;}
}
public class Module: ValueObject
{
private string title;
private string description;
private ModuleId id;
public constructor(id, title, description){...}
public string getTitle(){return this.title;}
public string getDescription(){return this.description;}
public string getId(){return this.title;}
}