背景
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了。
備注
考慮到聚合內非聚合根的實體都具有“本地標識”,采用“雙主鍵”就非常合理了,這個錯誤新手經常犯的。
再說一個題外話,聚合內的實體的標識能被其它聚合引用嗎?我覺得應該是可以的,前提是必須同時引用其完整的標識,即:雙主鍵。