在哪里验证唯一的 AggregateRoot 属性?
Where to validate unique AggregateRoot-properties?
我有一组 "large" 的 AggregateRoots,其中 属性 在其上下文中应该是唯一的。但是我在哪里验证呢?我想这取决于上下文是什么,据我所知,我有两个选择:
要么我在存储库服务中实施验证,以便持久性逻辑可以在保存聚合之前验证唯一属性(然后还必须同步此 AR 类型的所有保存)。
或者我将 "unique index" 移动到另一个聚合中作为聚合引用的字典,并让该字典验证唯一属性。由于我有大量的 AR,如果不实施这种方法可能会有问题,以便索引可以尽可能多地保留在磁盘上。
但是这里有真正的赢家吗?这两种方法都有效且使用安全吗?有什么主要缺点需要考虑吗?其他变体?
我的想法:
第一种方法可能更简单一些,但也更受限制。例如,如果需要的话,为同一个 AR 类型创建多个索引会更复杂。另一种方法更多地局限于单个聚合,我猜这更符合应该如何处理聚合。第一种方法要求所有这种类型的聚合都由同一进程保存,因为所有保存都必须同步。另一种方法不需要这个,而是引入这个索引聚合,所有保存都必须通过该索引聚合才能验证 属性 上的新值和更新值。此方法也不验证数据库中是否存在多个具有相同 属性 值的聚合,仅验证引用的聚合具有唯一属性。
聚合只关心它自己的一致性。它并不真正关心它如何与系统中的任何其他事物关联或相关,在它自己的边界之外。
如果您需要进行任何 cross-aggregate 检查,有两种选择 - 您需要重新考虑聚合边界,也许您当前的聚合只是更大聚合的实体。但是,如果您的事务范围是您当前作为聚合的范围,它将不起作用(尽管唯一性约束与此声明有点矛盾)。
但我们都知道臭名昭著的唯一用户名悖论。很明显,整个用户不能是一个单一的集合,但您需要确保用户名是唯一的。解决方案是在进行聚合之前检查应用程序服务的唯一性。如果您的查询存储完全一致,那应该永远不会有问题。如果你的写和读端不能保证同步,你仍然可以使用读端来保证唯一性,考虑到这个约束被违反的可能性。如果没有大爆炸,也没有小猫死亡,你可能会接受这样的情况,并在实际发生时处理约束违规,这可能是 never.
我有一组 "large" 的 AggregateRoots,其中 属性 在其上下文中应该是唯一的。但是我在哪里验证呢?我想这取决于上下文是什么,据我所知,我有两个选择:
要么我在存储库服务中实施验证,以便持久性逻辑可以在保存聚合之前验证唯一属性(然后还必须同步此 AR 类型的所有保存)。
或者我将 "unique index" 移动到另一个聚合中作为聚合引用的字典,并让该字典验证唯一属性。由于我有大量的 AR,如果不实施这种方法可能会有问题,以便索引可以尽可能多地保留在磁盘上。
但是这里有真正的赢家吗?这两种方法都有效且使用安全吗?有什么主要缺点需要考虑吗?其他变体?
我的想法:
第一种方法可能更简单一些,但也更受限制。例如,如果需要的话,为同一个 AR 类型创建多个索引会更复杂。另一种方法更多地局限于单个聚合,我猜这更符合应该如何处理聚合。第一种方法要求所有这种类型的聚合都由同一进程保存,因为所有保存都必须同步。另一种方法不需要这个,而是引入这个索引聚合,所有保存都必须通过该索引聚合才能验证 属性 上的新值和更新值。此方法也不验证数据库中是否存在多个具有相同 属性 值的聚合,仅验证引用的聚合具有唯一属性。
聚合只关心它自己的一致性。它并不真正关心它如何与系统中的任何其他事物关联或相关,在它自己的边界之外。
如果您需要进行任何 cross-aggregate 检查,有两种选择 - 您需要重新考虑聚合边界,也许您当前的聚合只是更大聚合的实体。但是,如果您的事务范围是您当前作为聚合的范围,它将不起作用(尽管唯一性约束与此声明有点矛盾)。
但我们都知道臭名昭著的唯一用户名悖论。很明显,整个用户不能是一个单一的集合,但您需要确保用户名是唯一的。解决方案是在进行聚合之前检查应用程序服务的唯一性。如果您的查询存储完全一致,那应该永远不会有问题。如果你的写和读端不能保证同步,你仍然可以使用读端来保证唯一性,考虑到这个约束被违反的可能性。如果没有大爆炸,也没有小猫死亡,你可能会接受这样的情况,并在实际发生时处理约束违规,这可能是 never.