DDD 和唯一性约束

DDD and uniqueness constraint

如何使用 DDD 验证唯一约束?假设一个 Entity 有一个 属性 name 在系统中必须是唯一的,并且有一个特定的 EntityRepository 方法 nameExists(name): bool... 这就是我发现人们建议这样做,因为存储库是所有 Entityies 集合的抽象,应该能够执行此检查。

所以在 creating/adding 新的 Entity 之前,命令/域服务可以针对存储库检查 newName 是否存在,但我认为这并不总是有效,因为并发性。

在同时启动两个事务的并发场景中,EntityRepositorynameExists 方法可能 return false 用于两个事务,并且由于这两个具有相同名称的条目将被错误地插入。

我确信我遗漏了一些基本的东西,但我找到的答案都指向存储库 exists 方法 - TBH 其他人说 UNIQUE 约束应该放在数据库上以捕捉并发情况,但是如果使用事件溯源或没有唯一约束的持久层怎么办?

| 跟进问题 |

如果要在层次结构的不同级别应用唯一性约束怎么办?

A Containername 在系统中必须是唯一的,然后 Child names 在 Container 中必须是唯一的。

假设事务数据库负责尽可能低的唯一性,那么域呢?

我是否还应该在域级别表达唯一性逻辑,例如使用用于系统级唯一性的域服务并将 Child 实体嵌入 Container 实体并具有业务规则(因此使 Container 成为聚合根)?

或者我不应该费心“复制”域中的唯一性并且(假定两者之间没有其他规则适用)拆分 ContainerChild?那么域名会不会缺乏表现力呢?

I am sure that I am missing something basic

不是基本的东西。

我们通常用于在一组实体中强制执行约束(如唯一性)的术语是 set validation。 Greg Young 请您注意一个具体问题:

What is the business impact of having a failure

大多数设置约束属于两类之一

  • 当系统达到稳定状态时需要为真的约束,但在工作进行时可能不成立。在业务流程中,这些通常是通过检测存储数据中的冲突,然后调用各种缓解流程来解决冲突来处理的。

  • 需要始终为真的约束条件。

第一类包括在飞机上重复预订座位;除非两个人都出现,否则这不一定是个问题,即使这样你也可以通过将某人撞到另一个座位或另一个航班来处理它。

在这些情况下,您会尽最大努力 - 查看该集合的最新副本,确保那里没有冲突,然后希望最好(接受一定比例的时间,您会错过了更改)。

参见 Memories, Guesses and Apologies(Pat Helland,2007 年)。

第二类是硬类;为确保不变量成立,您必须 锁定整个集合 以确保比赛不允许两个不同的作者插入冲突的信息。

关系数据库往往非常擅长集合验证 - 将整个集合放入单个数据库将是正确的答案(请注意假设集合足够小以适合单个数据库 - 尝试同时锁定两个数据库很难。

另一种可能性是确保在任何给定时间只有一个作者可以更新集合——当您是唯一的 运行 时,您不必担心输掉一场比赛。

有时您可以锁定一个较小的集合——例如,想象一下,有一组带有数字的锁,名称的哈希码会告诉您必须抓住哪一把锁。

这个最简单的版本是您可以将名称用作聚合标识符本身。

if one uses Event Sourcing or a persistence layer that does not have unique constraints?

有时候,你会引入一个专用于集合的持久化存储,只是为了确保你可以保持不变性。请参阅“微服务”。

但是,如果您不能更改数据库,并且不能使用具有所需锁定保证的数据库,并且业务绝对必须使集合始终有效...那么您将单线程那部分工作。

每个想要更改名称的人都会将请求放入队列中,负责管理不变量的线程会验证每个更改。

没有魔法;只是努力工作和权衡。