我们应该在持久化域模型时使用外键约束吗?

时间:2009-07-02 19:55:30

标签: language-agnostic design-patterns orm domain-model

不久前,我与同事讨论了Domain Models的持久性以及我们是否应该在数据库级别强制执行外键约束。

我的第一反应是关系数据库的使用意味着强制执行这些约束,但有些人认为数据库应该只被视为持久性机制,因此我们应该避免在其中放置任何业务逻辑。我们最终没有使用外键约束。

这是我的问题(我希望它不太通用):在这些情况下强制执行关键约束是否被视为良好做法?

6 个答案:

答案 0 :(得分:9)

强制执行约束,但不要在业务逻辑中依赖它们

  • 数据库上没有业务逻辑:我同意这个原则。如果您的非SQL 业务代码 依赖 来检查数据库的一致性,那么您应该重新考虑您的业务逻辑。
  • 向业务逻辑添加 数据库约束没有错。特别是因为像FOREIGN KEYs的参照完整性和其他UNIQUE约束这样的东西很容易做到,而且RDBMS非常有效地为你完成这项工作而不需要太多维护。
  • 您是否也不会在数据库中使用 indices ,因为它不是纯粹持久性相关的?
  • 找到并修复软件错误可能需要一些时间,但你肯定不想花更多时间清理或者(更糟)丢失一些数据,只是因为你省去了自己的麻烦为FK编写单行脚本。真的:你在这里免费获得一些东西并拒绝它吗?
  • [EDIT-1] :您能否保证通过您的应用程序管理数据库中的数据?似乎总是有例外,主要是高级用户,他们有时(非常罕见:-)犯错误并执行一些SQL语句来清理你的代码,更新状态(由于拼写错误导致无效值)等。
  • [EDIT-2] :构建域驱动的模型不是雇佣优秀数据库管理员的借口。使用ORM不是聘请优秀DB开发人员的借口。

但如果您和您的团队能够编写无错误的软件并处理代码中的所有可能的异常情况(包括硬件/网络/虚拟用户/程序员错误失败),那么“嘿,为什么要为多余的 FK约束而烦恼......” - -teaser -

答案 1 :(得分:7)

我是这么认为的,我认为这不是业务逻辑的一部分,而是防止将“坏”数据输入数据库。一旦数据库变得非常大,这些约束将防止将来出现令人头疼的问题。

当您有多个开发人员针对相同数据开发应用程序时,这将特别生效。这将确保他们也只能输入有效数据。将约束控制在1个点而不是x应用程序中肯定是有益的。

答案 2 :(得分:3)

我觉得忽略真正有用的工具(数据库级数据完整性)纯粹的开发方法是适得其反的。数据库真的很擅长这种事情......让他们去做。

在某些时候,每种方法都会开始崩溃,你必须要切实可行。

答案 3 :(得分:2)

我曾经这么认为,但自从我开始编写大量面向资源的系统以来,我的观点发生了变化。通常,验证一段数据不仅仅需要外键约束 - 例如,处于“已分配”状态的票证必须具有有效的“assigned_to”值,依此类推。所有这些规则都应放在某种验证例程中,理论上它可能不会伤害在数据库级别进行额外验证,如果你的应用程序级验证工作正常,检查外国关键约束只是浪费周期。更糟糕的是,您现在已经在两个地方重复了数据模型的逻辑 - 验证代码和数据库约束。

以这种方式思考:如果您不需要,您是否希望将任何其他应用程序逻辑移入数据库(例如,通过存储过程)?如果你没有因性能考虑而被迫这样做,我认为答案通常应该是“不”。

答案 4 :(得分:2)

“我的第一反应是关系数据库的使用意味着强制执行这些约束,但有些人认为数据库应该只被视为持久性机制,因此我们应该避免在其中放置任何业务逻辑。我们最终没有使用外键约束。“

是的,好吧,平庸的大多数人总是以数字的力量赢得那种辩论,唉。

如果你还想打那场战斗,你可能会问你的对手他们打算如何阻止任何人使用“直接数据库编辑器”(ala db2-aid,spufi,...)以及他们打算如何阻止任何人使用这些工具破坏数据库(根据定义绕过他们编程的业务约束)。

答案 5 :(得分:0)

如果您想遵循Domain Driven Design范例,那么对于Aggregate中的任何内容,答案都是肯定的,而对于任何交叉聚合链接,答案都是肯定的。

在几乎所有情况下,当删除Root本身时,您希望删除Aggregate Root下的任何内容,因此使用代表此项的外键(通过级联删除)可以在数据库级别实现此目的。如果您不想在数据库级别执行此操作,也可以让您的存储库执行级联删除操作,但仍然认为如果没有Root,则不应存在聚合子级。

对于跨聚合问题,您可能正在处理业务决策,以确定在删除其中一个或另一个时应该发生什么。通常,您需要异步处理此问题以实现可伸缩性,因此您的域模型最终会最终保持一致。因此,在这些情况下执行外键是没有意义的,因为会有一个时间窗口,其中一个或另一个键可能不存在。

希望有所帮助!有关更多信息,请务必查看Evans' book on Domain Driven Design - 以及网络上的许多链接。