DDD中如何为两个聚合根映射同一张数据库表?
问题背景
定义了值对象Operation,两个聚合根Doctor和Application都包含该值对象,使用EF Core的OwnsOne配置映射时,出现报错:
Cannot use table 'customer.Operations' for entity type 'Doctor' since it is being used for entity type 'Application' and potentially other entity types, but there is no linking relationship. Add a foreign key to 'Application' on the primary key properties and pointing to the primary key on another entity type mapped to 'customer.Operations'.
解决方案
方法1:将值对象嵌入聚合根表(推荐)
值对象在DDD中是聚合根的从属部分,无独立业务身份,最佳实践是将其字段直接嵌入到聚合根对应的数据库表中,无需单独建表。配置时无需指定ToTable,EF Core会自动在聚合根表生成值对象属性对应的列:
// Doctor 聚合根的映射配置 doctorBuilder .OwnsOne(x => x.Operation, operationBuilder => { // 可选:自定义嵌入列的名称(避免与其他字段冲突) operationBuilder.Property(o => o.Name).HasColumnName("Operation_Name"); operationBuilder.Property(o => o.Value).HasColumnName("Operation_Value"); }); // Application 聚合根的映射配置 applicationBuilder .OwnsOne(x => x.Operation, operationBuilder => { operationBuilder.Property(o => o.Name).HasColumnName("Operation_Name"); operationBuilder.Property(o => o.Value).HasColumnName("Operation_Value"); });
这种方式下,Doctors和Applications表会各自包含值对象的字段,完全避免表冲突,同时符合值对象的设计原则。
方法2:为每个聚合根的值对象指定独立表
如果业务需要单独存储值对象数据,可给每个聚合根的值对象配置不同的表名:
// Doctor的Operation映射到DoctorOperations表 doctorBuilder .OwnsOne(x => x.Operation, operationBuilder => { operationBuilder.ToTable("DoctorOperations"); // 配置外键关联聚合根,EF Core自动处理主键与关联逻辑 operationBuilder.HasForeignKey("DoctorId"); operationBuilder.Property<int>("Id").ValueGeneratedOnAdd(); }); // Application的Operation映射到ApplicationOperations表 applicationBuilder .OwnsOne(x => x.Operation, operationBuilder => { operationBuilder.ToTable("ApplicationOperations"); operationBuilder.HasForeignKey("ApplicationId"); operationBuilder.Property<int>("Id").ValueGeneratedOnAdd(); });
为什么不能用一对一关系?
一对一关系用于关联两个独立实体,而值对象是聚合根的组成部分,没有独立生命周期和业务身份。使用一对一关系会违背DDD值对象的设计原则,还会引入不必要的实体关联复杂度。
内容的提问来源于stack exchange,提问作者Pal

