我试图决定是否使用单个聚合,或者是否可以使用三个单独的聚合。
我有三个实体,Quiz,Question和Answer来代表多项选择题.从概念上讲,一个小测验包含多个问题,每个问题包含多个答案。每个答案只属于一个问题,每个问题只属于一个小测验。为每个用户创建一个单独的测试。
一条业务规则是,一旦提交了一个测试,答案就不能再被选中或取消选择。测试的分数是在提交时计算出来的,所以如果在提交测试后,答案发生了变化,则会使域处于不一致的状态。
如果我以Quiz作为聚合根目录,将其建模为单个聚合,则这非常简单,可以强制执行。如果我将这些实体中的每一个实体建模为自己的聚合根,那么在选择或取消选择答案之前,必须在应用程序级别而不是在域级别检查是否提交了一个测试。
这是我将在应用程序级别对单独的聚合所做的简化版本:
async execute({ quizId, answerId }: Request): Promise<Response> {
const quiz = await this.quizRepository.get(quizId);
if (quiz.submitted) {
throw new AnswerSelectQuizAlreadySubmitted();
}
const answer = await this.answerRepository.get(answerId);
answer.select();
return await this.answerRepository.save(answer);
}对于应用程序级别的单个聚合,我要做的是:
async execute({ quizId, questionId, answerId }: Request): Promise<Response> {
const quiz = await this.quizRepository.get(quizId);
// quiz entity enforces the business rule
quiz.selectAnswer(questionId, answerId);
return await this.quizRepository.save(quiz);
}我更喜欢小聚合模型,因为用户可以同时更新多个答案,而不会因为Quiz级别的乐观锁定而导致数据库故障。
我担心的是,一个“提交测试”命令和一个“选择答案”命令可能会被快速地连续发射,导致在已经提交的测试中选择一个答案。我的直觉是,我上面展示的应用程序级验证并不一定会阻止这种情况的发生。
TL;DR:应用程序级别的检查是在异步环境中工作,还是强制将所有三个实体都包含在一个聚合中?。
发布于 2021-07-27 15:08:13
这一条语句
每个答案只属于一个问题,每个问题只属于一个小测验。
整个问题的其余部分都没有意义。答案是,您将在域模型中使用单个聚合。
如何处理数据库争用是一个单独的(尽管很重要)问题,也许可以通过使用文档数据库(其中文档是聚合根及其子根)来解决。
如果你的域名更丰富的话,我的答案就会不同。
例如,您可以有一个 example 聚合,其中包含一个应答列表和一个ValidAnswer。如果您随机生成一个QuizQuestion Quiz,并从一个问题聚合列表中选择,那么您将有一个Quiz聚合,其中包含一个作为子级的(或QuesionReference或其他类似的)列表。因此,您的QuestionReference. Quiz聚合引用(未拥有/单独聚合)通过(拥有/值对象)询问在这个场景中,您可以安全地避免某些争用,因为您仍然在更新Quiz聚合,而不是 because 聚合。
处理点击满意的用户
在这两种情况下,您的域模型都必须检查答案选择不能在提交测试后发生。这是Quiz聚合的逻辑。
现在,这就是说,您对可能发生的种族状况的关注是有效的。简单的模型很容易;单个事务或单个文档的单个聚合自然会防止争用条件;只需使用普通的db并发选项即可。但是,两个聚合模型也是如此!为什么?您仍然只更新Quiz聚合。问题聚合被引用,并且不会改变(至少在问答上下文中是如此)。
最后,在上述所有内容中, separate 似乎不像是一个单独的聚合或实体。如何回答的问题而不与问题联系在一起?也许在危险!
https://stackoverflow.com/questions/68545678
复制相似问题