DDD:主键(Ids)和ORM(例如,NHibernate)

And*_*kin 17 architecture nhibernate domain-driven-design

为什么在域实体中有一个Id字段被认为是可以的?我已经看到几个解决方案,它们提供基于Id和Id的GetHashCode/Equals的基类.

我对域模型的理解是它应该只包含与域相关的东西.虽然在极少数情况下(可跟踪订单)ID是有意义的,但大多数情况下它们除了在DB/UI上引用对象的简单方法之外不提供任何其他内容.

我也没有看到Equals/GetHashCode的好处,因为Identity Map实现应该保证引用相等性无论如何都是 id相等.

奇怪的是,我不能轻易找到其他人对这个问题的看法,所以我在这里问.在域实体中使用非域相关ID的一般意见是什么?如果我不向我的域实体添加ID,那么NHibernate有任何问题吗?

更新:

谢谢你的回答.

其中一些建议使用Id是ORM进行数据库更新的唯一方法.我不认为是这种情况.ORM已经跟踪从DB加载的所有实体,因此它应该能够在需要时从内部轻松获取Id.

更新2:

回答正义和类似的观点: 如果我们有一个Web应用程序并且需要一种在会话之间引用实体的方法怎么办?喜欢edit/resource/id?

好吧,我认为这是受约束的UI /环境的特定需求,而不是域模型的需要.使用GetIdentitity方法(与Load(身份)方法一致)的应用程序服务或存储库似乎足以满足此方案.

Ste*_*ger 4

我只能谈谈 NHibernate。在那里,您需要一个主键字段,如果您采用业务数据(不推荐)或没有业务意义的代理键,则由您决定。

典型场景有:

  • 数据库生成的自动递增值
  • NHibernate 生成的 guid
  • 由业务逻辑生成的guid

拥有唯一的 id 有一个很大的优势。当您传递对象时,例如传递给客户端并返回到服务器,您不能依赖内存标识。您甚至可以在同一业务实例的内存中拥有多个实例。业务实例通过id来标识。

基于 ID 的比较是一个品味问题。我个人喜欢简单可靠的代码,基于id的比较简单可靠,而深度比较总是有点风险,容易出错,不可维护。因此,您最终会使用运算符 == 比较内存标识,使用 Equals 比较业务(和数据库)标识。

据我所知,NHibernate 是将类模型映射到关系数据库的侵入性较小的方法。在我们的大型项目中,我们有主键和版本属性(它们用于乐观锁定)。您可能会说这是侵入性的,因为它不用于业务逻辑。

NH 不需要具有这些属性。(但是它需要其中一个属性作为主键。)但请考虑:

  • 它只是工作得更好,例如。悲观锁定只有在适当的属性下才可能实现,
  • 更快:int id 在许多数据库上比其他数据类型执行得更好
  • 而且更容易:出于多种原因,不鼓励收购对业务有意义的财产。

那么为什么要让你的生活变得不必要的困难呢?


编辑:刚刚阅读文档,发现 NHibernate 不需要持久类中的 id 属性!如果没有标识符,则无法使用某些功能。建议拥有它,它只会让您的生活更轻松。

  • 我明白你为什么不推荐使用业务数据键的观点,但我最初的问题是关于使用代理键——但仅限于它所属的数据库。因此,如果 NHibernate 知道代理数据库键,那么这些缺点就不适用——我只是不想在我的域实体中定义它。 (2认同)
  • 据我所知,无论如何都不建议传输域对象——这就是 DTO 的用途。大多数时候,当我有一个 Web 应用程序时,我将 ids 视为基础设施问题,可委托给基础设施——例如repository.GetId(entity)。当我们想要返回对象时,我们已经做了同样的事情,它是repository.Load(),而不是entity.Load()。那么为什么是entity.Id呢?我知道可以通过补丁来添加,我只是更感兴趣是否有一些重要的架构原因优先使用 Id。 (2认同)