使用CQRS和事件溯源时的唯一性验证

21

我正在尝试使用事件溯源实现自己的CQRS基础架构,以便更好地学习它。作为一个示例项目,我正在实现一个博客引擎,我知道它可能不是一个完美的选择,但我只是想在一个真实的项目上工作。

我现在遇到的问题是验证。每篇文章都有一个shortUrl,而且shortUrl 应该是唯一的,那么在领域模型中应该放在哪里进行验证呢? 我知道在发送创建或更新文章命令之前会通过读取我的读存储来检查是否有效来执行此验证。

我能想到两种“解决方案”:

  1. 拥有一个Blog聚合,用于跟踪所有与博客相关的设置和所有文章的引用。但我认为这种情况下的问题是我必须处理聚合之间的通信,以及每次需要验证shortUrl的唯一性时,我需要从事件存储中读取所有事件来创建所有文章,这似乎太复杂了。
  2. 我有的第二个选择是当事件被触发并且我的事件处理程序创建读模型时,如果它注意到将有两个指向不同文章的短链接,那么它会触发重复的短链接事件。在检测到错误时,让读模型触发事件是否有效呢?

是否还有其他选择呢?请注意,我知道我的域可能不是cqrs和DDD的最佳选择,但我正在一个小领域中做这件事来学习。


请看:https://dev59.com/DXE85IYBdhLWcg3wJQKt - Rich Apodaca
3个回答

4
我会选择一个应用程序服务,该服务只负责生成唯一的短链接。您可以使用事务性数据库来实现此行为。通常,该服务将由BlogPost集合的命令处理部分使用。如果存在重复的ShortURL,则可以触发DuplicateUrlErrorEvent。您可以通过使用相同数据源创建一个轻量级查询模型,在UI中预先捕获这个错误(但永远不会100%准确),以便在提交帖子之前查询短链接是否唯一(如@RyanR's答案所述)。

3
我已经阅读了关于这个问题的各种答案。
决定取决于正确性。如果您可以宽容并接受某些操作的不完美行为,那么您的问题更容易解决,特别是在弱一致性保证下。
然而,如果您想要一致性,则应使用具有强一致性保证的持久化服务。
例如,创建短网址的命令将验证读取存储是否已包含此类短网址,并且只有在我们可以首先提交更改到我们的读取存储时,我们才会提交我们的事件。
如果我们可以将更改提交到我们的读取存储,我们就没有违反任何唯一性约束(假设您的读取存储强制执行此类约束),然后我们可以继续。
但是,由于我们有两个事务不一定在同一个数据库上,因此我们可能在第一次提交后失败。这没关系,因为整个操作也会失败。读取存储将在一段时间内反映出不一致的状态,但是只要我们修复聚合物,读取存储就会回到一致的状态。
作为维护程序,我们可以定期修复可能存在错误的聚合物。您可以通过引入仅在两个事务都成功提交后才清除的错误标志来实现此操作。
有一个例子,银行允许用户透支他们的账户,因为他们有补偿。这引发了问题,因为解决这个问题似乎很草率,甚至懒惰。有些人称之为聪明。我不知道该怎么想。银行可能有足够的钱来支付,所以他们可以忽略它,但这不是当前世界的工作方式。无论如何,我离题了。
从正确性的角度来看,我们的读取存储具有强一致性保证,并且我们将以这样的方式编写我们的投影,即如果余额变为负数,则无法将交易提交到读取存储。因此,最糟糕的情况是从读取存储中扣除了费用,但是操作从未完全提交到事件存储中。用户将看到其帐户中缺少的金额,直到维护程序注意到错误标志并修复帐户。我认为这是一个可行的妥协方案。

0

这要看“业务”想要发生什么。如果你希望客户端(命令的创建者)负责选择一个短网址,则应该具有一个读取存储库,以验证其唯一性。当用户输入一个短网址时,视图应检查短网址是否唯一,如果不是,则呈现验证错误。每当保存帖子时,事件都会发布更新的信息(包括短网址),从而使读取存储库保持同步。


1
这就是我所说的“我知道在发送命令之前,通过从读取存储中读取以检查是否有效来创建创建帖子命令或更新帖子命令。”但这并不能保证在领域中它仍然是唯一的。当创建博客文章时,这种情况很不可能发生,但我认为这是一个有趣的场景,我仍然需要弄清楚如何在使用CQRS时解决它。我的意思是如何处理领域中的检查? - Tomas Jansson
在我正在工作的系统中,数据存储中存在唯一约束条件。因此,如果发生两个不同实体同时保存具有冲突字段的极端情况,该约束条件会导致异常冒泡到用户界面,从而可以手动修复。除非领域逻辑可以纠正该场景,否则必须由人工处理。 - RyanR

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