聚合内的聚合列表

List of aggregates inside an aggregate

我正在使用 DDD 聚合对学生和出勤功能进行建模。我最终得到的是 Student class 的出勤记录列表。我正在使用 EF 核心加载学生以及与学生相关的所有出勤率。

所以每天都会有一个学生的出勤记录。出勤记录可以在一天内多次更新。我最终在 Student class 上创建了一个名为 CreateOrUpdateAttendance(Date date, AttendanceDetails attendance) 的 public 方法,如果不创建新的出勤实例,它将在内部检查给定日期是否有任何出勤记录,使用提供的出勤详细信息更新此出勤记录添加/更新与学生相关的出勤列表。因此,每次为学生更新/创建出勤时,都会从数据库中查询学生实体以及所有出勤记录。根据从输入中收到的 Id 查询学生,以检查提供的 Id 是否有效。

问题是随着时间的推移,会有很多与学生关联的出勤记录,这意味着不必要地提取与学生关联的所有出勤记录,这将导致查询性能不佳。

这里的 Student 是正确的词根吗?遗憾的是,EF 核心尚不支持过滤子对象。

我的问题是如何处理需要在聚合内更新子实体的用例更具体地说是聚合内实体列表中的单个实体?

在这种情况下,我应该将出勤率视为不同的总和吗?这样我就可以独立于学生记录查询考勤记录了?

我最后两次调用数据库,一个是检查学生(没有加载出勤率),另一个是获取出勤记录。

我在这里遗漏了什么?我应该重新考虑我的设计吗?任何指导都会很有帮助。

Is Student the proper root here?

聚合是一个一致性边界。它的存在是为了确保其中包含的实体处于与您的业务规则相关的一致(即有效)状态。您尚未描述任何应确保的业务规则或验证,因此我不知道 Student 是正确的聚合。继续阅读...

Unfortunately EF core doesn't support filtering child objects yet.

EF Core 不支持过滤子集合,但可能有其他方法可以实现此目的。这是一些伪代码:

var query = 
    from s in ctx.Students
    where s.Id == 1 // Student filter here
    select new 
    {
        Student = s,
        Student = 
            from sa in s.StudentAttendances
            where sa. // Apply filter here
            select sa
    };

// Perform the query against the DB and then return the Student object
// EF will wire up the relationships for you and because you went to the
// DB first before pulling the student, the data is local and you will
// have the full tree
var student = query.ToList().Select(x => x.Student).FirstOrDefault();

In this case should I treat the Attendance as different aggregate? So that I can query the attendance record independent of the student record?

这取决于您的业务规则。 Student 和 Attendance 的寿命是多少?学生可以有任意数量的出勤吗?多考虑您的写入用例,少考虑您的读取用例。您是否需要加载 StudentAttendances?那只是一个阅读用例吗?

还要考虑一个 Student Aggregate 是否足够。当您只想更改 phone 号码时是否应该加载所有考勤记录?正如我们有限界上下文一样,您可能会考虑使用有界聚合,例如限界上下文中聚合的垂直分区,将听起来像一个单一的大概念拆分为几个较小的概念。

您可以拥有:

  • 具有过滤出勤列表的学生汇总(如上图所示)
  • 学生出勤汇总(如您所述)
  • 一个 StudentWithAttendance 聚合,它与 Student 聚合分开并服务于不同的用例

I am missing something here? Should I rethink my design?

视情况而定。一般来说,我建议多考虑域级别,少考虑数据库级别。多想想写而不是读。

EF 在某些方面迫使我们以关系的方式思考,即使我们在为我们的领域建模时也是如此,但我们应该与这种冲动作斗争,并尽可能将 EF 降级为一个简单的持久性机制,而不是让它驱动我们的领域设计决策.