DDD之聚合在.Net中的实现
# DDD之聚合在.Net中的实现
(1)工作单元
EFCore的DbContext:跟踪对象状态的改变;SaveChanges把所有的改变一次性地提交到数据库中,是一个事务。因此DbContext是天然的UoW实现。
(2)聚合与聚合根的实现
- 即使一个实体类型没有声明对应的DbSet类型的属性,只要EF Core遇到实体对象,EF Core仍然会像对待其他实体对象一样处理。
- 我们可以在上下文中只为聚合根实体声明DbSet类型的属性。对非聚合根实体、值对象的操作都通过根实体进行。
- 跨聚合只能引用根实体的Id,而不是根实体对象。
(2)聚合与DbContext的关系
-
如果一个微服务中有多个聚合根,那么是每个聚合根的实体放到一个单独的上下文中,还是把所有实体放到同一个上下文中。
-
各个聚合跟之间的关系仍然比它们和其他微服务中的实体关系更紧密,而且我们还会在应用服务中进行跨聚合的组合操作。进行联合查询的时候可以获得更好的性能,也能更容易实现强一致性的事务。
(3)区分聚合根实体和其他实体
- 定义一个不包含任何成员的标识接口,比如IAggregateRoot,然后要求所有的聚合根实体类都实现这个接口。
(4)跨表查询
- 所有跨聚合的数据查询都应该是通过领域服务的协作来完成的,而不应该是直接在数据库表之间进行join查询。会有性能损失,需要做权衡,不是死规矩
- 对于统计、汇总等报表类的应用,则不需要遵循聚合的约束,可以通过执行原生SQL等方式进行跨表的查询。
(5)实现实体不要面向数据库建模
- 建模的时候不要先考虑实体在数据库中如何保存。比如实体类和数据表具有直接的对应关系,实体类中属性和数据表中的列几乎完全一致。这样设计出来的类称不上“实体类”,只能被成为数据对象(Data Object)。更不要用DB First(反向工程)。
- 应该不考虑数据库实现的情况下进行领域模型建模,然后再使用Fluent API等对实体类和数据库之间做适配。在实现的时候,可能需要对建模进行妥协性修改,但是这不应该在最开始被考虑。