DDD - 验证唯一约束

18

在DDD中,你永远不应该让你的实体进入无效状态。话虽如此,那么如何处理唯一约束的验证呢?

创建实体并不是真正的问题。但是假设你有一个实体必须拥有唯一的名称,并且存在一千个这种实体类型的实例(它们不在内存中但存储在数据库中)。现在假设你想要重命名一个实例。

你不能只使用setter……对象可能会进入无效状态——你必须对数据库进行验证。

在Web环境中,你如何处理这种情况?

2个回答

21

唯一性约束可以转化为持久性异常,而不是被视为"无效状态"。除非对象被持久化,否则它不是无效状态。唯一性只在持久性的情况下才有意义。现实情况是,在验证机制中加入此类规则可以帮助减少此错误的可能性,但在任何实际的多用户系统中,直到成功完成持久性操作,您不能保证唯一性。

因此,您可能希望在您的验证机制中使用这种规则,但必须在持久层中执行。

我通常支持DDD作为一种方法论,但我认为“不允许对象进入无效状态”可能需要一些繁琐的抽象。在Web应用程序中,拥有一个单独的“View Model”是一种可能的解决方案,作为持久化之前的中介层,但我通常不会这样做,除非我确信它会给我带来比更简单的选择更少的痛苦。


谢谢你的回答。我基本上同意你所说的,但我想听听纯粹主义者对“唯一性约束可以简化为持久性异常”这个观点的看法 - 而不是无效状态... 对于视图模型,我几乎总是使用它,但在某个时刻,您将不得不使用视图模型中的数据更新您的“真实”模型,您仍然会面临同样的问题。 - W3Max
1
DDD并不是一种宗教;它并不是关于纯洁度,而是关于清晰度。在某种程度上,它确实与纯洁度有关,你会发现我的思考大部分时间都与规范的DDD非常接近。如果你想在持久层之外解决唯一性问题,你可以尝试调整规范模式以适应你的需求。 - JasonTrue
我不确定我完全同意这里的答案。我认为您不应该依赖持久层来执行业务规则。这将扩散域逻辑到持久层。相反,考虑将唯一性检查作为用例的一部分。有些唯一性约束不能在持久层上执行。例如,“一个部门只能有2名经理”的验证无法在持久层上完成。 - alaboudi
如上所述,您可能希望在域层中验证唯一性,但除非您的域层存在一些非典型的扭曲来打破会话边界,否则即使是您的反例也无法仅通过域层在多用户/多节点系统中安全地执行。此外,在数据库层面上强制执行您的示例也是可能的。您可以在大多数良好的关系型数据库上使用CHECK或可延迟约束,或在能力较弱的数据库上使用TRIGGER;https://www.postgresqltutorial.com/postgresql-check-constraint/ - JasonTrue

3

在寻找答案时,我找到了这篇文章:https://thinkbeforecoding.com/post/2009/10/28/Uniqueness-validation-in-CQRS-Architecture

简而言之:寻找唯一性的范围,并在代表该范围的聚合根中存储唯一值的权威列表。

例如,对于用例“当注册新用户时,所请求的登录必须是唯一的”,这将是:登录必须在租户(或公司、国家等)内是唯一的。在代表租户的聚合根中存储有关给定登录的信息。

通过这种方式,可以确保租户永远不会进入无效状态。


9
尽管这确实解决了问题,但如果你有数百万用户在一个单一的聚合下,那么这就不再高效,无论有多么优雅。 - Greyshack
1
@Greyshack,虽然这可能是真的,但这可能表明您的聚合物设计不正确。对于真正的“数百万”用户,您仍将不得不采用最终一致性。 - istepaniuk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接