背景
DDD中只有聚合根可以有仓储,仓储负责整个聚合持久化的相关生命周期,在不使用工作单元或POCO的情况下,我们可以让Order内部直接调用DAL操作OrderItem。我们也可以让Order跟踪所有OrderItem的状态,然后在OrderRepository内部操作OrderItem。如果我们采用了重量级的ORM工具,如:EntityFramework,事情会不会变得简单呢?
使用EntityFramework持久化聚合
关键思路:双主键。
示例聚合
这里以订单和订单项为例。
Order管理OrderItem
1 public void AddOrderItem(OrderItem item) 2 { 3 item.MustNotNull("item"); 4 5 this.State.BeforeChangeOrder(); 6 7 item.OrderId = this.Id; 8 this.OrderItemCollection.Add(item); 9 this.Total += item.Subtotal; 10 } 11 12 public void RemoveOrderItem(Guid productId) 13 { 14 this.State.BeforeChangeOrder(); 15 16 var item = this.OrderItemCollection.First(x => x.ProductId == productId); 17 this.OrderItemCollection.Remove(item); 18 this.Total -= item.Subtotal; 19 }
映射配置
1 modelBuilder 2 .Entity<OrderItem>() 3 .HasKey(x => new { x.Id, x.OrderId }); 4 5 modelBuilder 6 .Entity<Order>() 7 .HasKey(x => x.Id); 8 modelBuilder 9 .Entity<Order>() 10 .Property(x => x.OptimisticKey).IsRowVersion().IsConcurrencyToken(); 11 modelBuilder 12 .Entity<Order>() 13 .HasMany(x => x.OrderItemCollection).WithRequired().HasForeignKey(x => x.OrderId).WillCascadeOnDelete();
注意:上面为OrderItem配置了双主键,如果不这么配置,删除逻辑会错误,要么删除后提交失败,要么OrderItem的数据库记录还在,只是OrderId变为NULL了。
备注
考虑到聚合内非聚合根的实体都具有“本地标识”,采用“双主键”就非常合理了,这个错误新手经常犯的。
再说一个题外话,聚合内的实体的标识能被其它聚合引用吗?我觉得应该是可以的,前提是必须同时引用其完整的标识,即:双主键。