0.引言
在现有的系统开发中,大部分的系统应该都会用到ORM,无论用的是EF还是NHibernate。作为对象和持久化数据的桥梁,ORM确实非常方便,以至于在DDD的时候,我们很自然的将 ORM中的Model(实体)表达成DDD中的 DomainModel(领域对象)。
但这真的合理吗?我们先引入两个例子来探讨这个问题。
1.例子1:订单聚合
下述聚合引自汤神的博客:
我们看以上的聚合设计非常经典。Order对象作为聚合根,OrderItem建模成实体,只要在当前的订单聚合中不重复即可。
但在真正的数据存储的时候,我们的OrderItem对象肯定不能是这样子的。
假如:我们有两个订单A和B,订单A包含了商品1和商品2,订单2包含商品2和商品3。
那么很明显我们如果以OrderItem这个实体去存储,必然会造成主键重复。在实际存储的时候我们肯定也会为OrderItem增加其他的字段用来存储他自己的主键信息。
比如我们给他建立一个独立的ID:
public class OrderItem{/// <summary>/// 订单项ID/// </summary>public string Id { get; set; }public string ProductId { get; set; }public string ProductName { get; set; }public float Price { get; set; }public int Count { get; set; }}
或则采用联合主键:
public class OrderItem{/// <summary>/// 订单ID/// </summary>public string OrderId { get; set; }public string ProductId { get; set; }public string ProductName { get; set; }public float Price { get; set; }public int Count { get; set; }}
2.例子2:旅馆聚合与房间聚合
在旅馆信息系统管理里面,对旅馆的操作会有独立的模块,参考聚合的设计原则
- 如果领域内的一个对象,我们会在后台有一个独立的模块去管理它,那它基本上也是聚合根了;
所以我们建立旅馆聚合,代码如下:
public class Hotel{/// <summary>/// 聚合跟id/// </summary>public string Id { get; set; }/// <summary>/// 值对象/// </summary>public string Name { get; set; }/// <summary>/// 房间列表 此处简单标示 /// </summary>public IList<string> Rooms { get; set; }//其他信息略//...}
对于旅馆房间的也一样我们会有单独的模块去管理,而且房间有单独的状态标示(房间是否有人入住,是否空房等等)
房间聚合如下:
public class Room{/// <summary>/// 房间id/// </summary>public string Id { get; set; }public string Name { get; set; }public string RoomType { get; set; }// 空 已预订,已入住,脏房间等状态public RoomStatus Status { get; set; }/// <summary>/// 旅馆聚合根ID/// </summary>public string HotelId { get; set; }}
如果是在ORM中上述很可能表达成如下实体:
public class Hotel{public string Id { get; set; }public string Name { get; set; }/// <summary>/// 房间列表 /// </summary>public virtual IList<Room> Rooms { get; set; }//其他信息略//...}
和房间实体
public class Room{/// <summary>/// 房间id/// </summary>public string Id { get; set; }public string Name { get; set; }public string RoomType { get; set; }// 空 已预订,已入住,脏房间等状态public RoomStatus Status { get; set; }/// <summary>/// 旅馆/// </summary>public virtual Hotel Hotel { get; set; }}
很明显的可以看出,上述模型不是DDD中的领域对象模型。
3.结论:ORM中的Model不应该与DDD中的DomainModel等价
更多的:我们在设计数据库表的时候,为了查询性能考虑,会冗余一些信息等等。
通过以上的分析,我们可以得出结论:ORM中的Model不应该与DDD中的DomainModel等价。
我藉著本文来抛砖引玉。
国外的大牛写的:Just-Stop-It!-The-Domain-Model-Is-Not-The-Persistence-Model.aspx