9

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

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

这是我的问题(我希望它不是太笼统):在这些情况下强制执行关键约束是否被认为是一种好习惯?

4

6 回答 6

11

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

  • 数据库上没有业务逻辑:我同意这个原则。如果您的非 SQL业务代码依赖于数据库约束来检查您的数据库一致性,那么您应该重新考虑您的业务逻辑。
  • 除了业务逻辑之外,拥有数据库约束并没有错。尤其是因为使用 FOREIGN KEY 和其他 UNIQUE 约束的参照完整性之类的事情很容易做到,而且 RDBMS 可以非常有效地为您完成这项工作,而无需太多维护。
  • 您是否也不会在数据库上使用索引,因为它不是纯粹与持久性相关的?
  • 查找和修复软件错误可能会花费您一些时间,但您绝对不想花更多时间清理或(更糟)丢失一些数据,仅仅因为您省去了为 FK 编写单行脚本的麻烦。真的:您在这里免费获得一些东西并且您拒绝它?
  • [EDIT-1]:你能保证你的数据库中的数据只能通过你的应用程序来管理?似乎总是有例外,主要是高级用户,他们有时(很少:-)会犯错误并执行一些 SQL 语句来清理代码,更新状态(由于拼写错误而导致无效值)等。
  • [EDIT-2]: Building domain driven model is not an excuse not to hire a good DB admin. Using ORM is not an excuse not to hire good DB developer.

But if you and your team are able to write bug-free software and handle all possible exception scenarios in your code (including hardware/network/dummy-user/programmer-error failures), then "Hei, why bother with redundant FK constraints...." - -teaser-

于 2009-07-03T05:00:17.330 回答
7

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

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

于 2009-07-02T19:59:26.100 回答
3

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

在某些时候,每种方法都开始崩溃,您只需要实用即可。

于 2009-07-02T20:01:22.307 回答
2

我以前也是这么想的,但是自从我开始写很多面向资源的系统后,我的看法发生了变化。通常,验证一条数据不仅仅需要外键约束——例如,处于“已分配”状态的票证必须具有有效的“已分配给”值,等等。所有这些规则都应该放在某种验证例程中,虽然理论上在数据库级别进行额外验证可能不会有什么坏处,但如果您的应用程序级别验证正常工作,检查外键约束只是浪费循环。然而,更糟糕的是,您现在已经在两个地方重复了有关数据模型的逻辑——验证代码和数据库约束。

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

于 2009-07-02T20:20:02.830 回答
2

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

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

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

于 2009-07-02T21:50:35.783 回答
2

如果您想遵循领域驱动设计范式,那么对于聚合中的任何内容,答案都是肯定的,而对于任何跨聚合链接的答案都是否定的。

在几乎所有情况下,您都希望在删除 Root 本身时删除 Aggregate Root 下的任何内容,因此使用表示此内容的外键以及级联删除,您可以在数据库级别实现此目的。如果您不想在数据库级别执行级联删除,您也可以让您的存储库自己执行级联删除,但重点仍然是聚合子级不应该在没有根的情况下存在。

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

希望有帮助!如需更多信息,请务必查看Evans 的领域驱动设计一书- 以及网络上的许多链接。

于 2009-07-03T03:58:14.377 回答