不久前,我与同事讨论了域模型的持久性以及我们是否应该在数据库级别强制执行外键约束。
我的第一反应是,关系数据库的使用本身就意味着强制执行这种约束,但有些人认为数据库应该被视为一种持久性机制,因此我们应该避免在其中放置任何业务逻辑。我们最终没有使用外键约束。
这是我的问题(我希望它不是太笼统):在这些情况下强制执行关键约束是否被认为是一种好习惯?
不久前,我与同事讨论了域模型的持久性以及我们是否应该在数据库级别强制执行外键约束。
我的第一反应是,关系数据库的使用本身就意味着强制执行这种约束,但有些人认为数据库应该被视为一种持久性机制,因此我们应该避免在其中放置任何业务逻辑。我们最终没有使用外键约束。
这是我的问题(我希望它不是太笼统):在这些情况下强制执行关键约束是否被认为是一种好习惯?
强制执行约束,但不要在业务逻辑中依赖它们
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-
我认为是的,我认为它不是业务逻辑的东西,而是防止将“坏”数据输入数据库。一旦数据库变得非常大,这些约束将防止将来出现问题。
当您有多个开发人员针对相同数据开发应用程序时,这尤其会生效。这将确保他们也只能输入有效数据。将约束控制在 1 个位置而不是 x 个应用程序中当然是有益的。
我觉得为了纯粹的开发方法而忽略真正有用的工具(数据库级数据完整性)会适得其反。数据库真的很擅长这种事情……让他们去做吧。
在某些时候,每种方法都开始崩溃,您只需要实用即可。
我以前也是这么想的,但是自从我开始写很多面向资源的系统后,我的看法发生了变化。通常,验证一条数据不仅仅需要外键约束——例如,处于“已分配”状态的票证必须具有有效的“已分配给”值,等等。所有这些规则都应该放在某种验证例程中,虽然理论上在数据库级别进行额外验证可能不会有什么坏处,但如果您的应用程序级别验证正常工作,检查外键约束只是浪费循环。然而,更糟糕的是,您现在已经在两个地方重复了有关数据模型的逻辑——验证代码和数据库约束。
可以这样想:如果没有必要,您是否希望将任何其他应用程序逻辑移入数据库(例如,通过存储过程)?如果您不是出于性能考虑而被迫这样做,我认为答案通常应该是“不”。
“我的第一反应是,关系数据库的使用本身就意味着强制执行这种约束,但有些人认为数据库应该被视为一种持久性机制,因此我们应该避免在其中放置任何业务逻辑。我们最终通过不使用外键约束。”
是的,好吧,平庸的大多数人总是仅仅靠数字的力量赢得这种辩论,唉。
如果你仍然想打那场战斗,你可能会问你的对手他们打算如何阻止任何人使用“直接数据库编辑器”(ala db2-aid、spufi、...)以及他们打算如何阻止任何人破坏数据库使用此类工具(根据定义绕过其编程的业务限制)。
如果您想遵循领域驱动设计范式,那么对于聚合中的任何内容,答案都是肯定的,而对于任何跨聚合链接的答案都是否定的。
在几乎所有情况下,您都希望在删除 Root 本身时删除 Aggregate Root 下的任何内容,因此使用表示此内容的外键以及级联删除,您可以在数据库级别实现此目的。如果您不想在数据库级别执行级联删除,您也可以让您的存储库自己执行级联删除,但重点仍然是聚合子级不应该在没有根的情况下存在。
对于跨聚合问题,您可能会处理业务决策,即当其中一个或另一个被删除时应该发生什么。通常,您会希望异步处理此问题以实现可伸缩性,因此您的域模型最终会保持一致。因此,在这些情况下强制执行外键是没有意义的,因为会有一个时间窗口,其中一个或另一个键可能不存在。
希望有帮助!如需更多信息,请务必查看Evans 的领域驱动设计一书- 以及网络上的许多链接。