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

时间:2012-02-26 17:26:09

标签: validation domain-driven-design cqrs

我正在尝试使用Event Sourcing实现我自己的CQRS基础架构,以便更好地学习它。作为一个示例项目,我正在实现一个博客引擎,我知道它可能不是一个完美的契合,但我只想做一些真实的事情。

我现在遇到的问题是验证。每个帖子都有shortUrlshortUrl应该是唯一的,但我应该在哪里将此验证放在域中?我知道在我通过读取读取存储器发送命令之前我将进行验证,以检查在创建create post命令或更新post命令时它是否有效。

我可以想到两个“解决方案”。

  1. 有一个Blog聚合,用于跟踪所有与博客相关的设置以及对所有帖子的引用。但是在我看来这个问题是我必须在那个场景中处理聚合之间的通信,以及每当我需要验证shortUrl的唯一性时,我需要读取从事件存储到事件存储的所有事件。创建所有帖子,这似乎很复杂。
  2. 我遇到的第二种选择是当事件被触发时,我创建读取模型的事件处理程序会在发现它有两个指向不同帖子的短网址时触发重复的短网址事件。读取模型在检测到错误时触发事件是否有效?
  3. 还有其他选择吗?请注意,我知道我的域名可能不适合cqrs和DDD,但我这样做是为了在小域中学习。

3 个答案:

答案 0 :(得分:2)

我会选择一个只负责生成独特ShortURL的域名服务。您可以使用事务DB来实现此行为。通常,此服务将由BlogPost聚合的命令处理部分使用。如果存在重复的ShortURL,则可以触发DuplicateUrlErrorEvent。 您可以通过使用相同的数据源创建精简查询模型来预先在UI中捕获这个(但绝不是100%),这样您就可以在提交帖子之前查询缩短的URL是否唯一(如@ RyanR的回答所述) )。

答案 1 :(得分:1)

我已经阅读了关于这个和相关问题的各种答案。

决定归结为正确性。如果您可以宽容并接受某种程度的操作的不完美行为,那么您的问题就更容易解决,特别是在弱一致性保证下。

但是,如果您想要一致性,则应使用具有强一致性保证的持久性服务。

例如,创建短URL的命令将验证读取存储不包含这么短的URL,如果我们可以先将更改提交到读取存储,我们将只提交事件。

如果我们可以将更改提交到读取存储,我们没有违反任何唯一性约束(假设您的读取存储实施了这样的约束),然后我们就可以继续了。

但是,由于我们有两个事务不一定在同一个数据库上,所以在第一次提交后我们可能会失败。这没关系,因为整个操作也会失败。读存储将反映一段时间内的不一致状态,但只要我们修复聚合,读存储就会恢复到一致状态。

作为维护程序,我们可以定期修复可能存在错误的聚合。您可以通过引入错误标志来实现此目的,只有在两个事务成功提交时才会清除该错误标志。

有一个例子,银行会允许用户透支他们的帐户,因为他们有附加费用来补偿。这引发了一些问题,因为解决这样的问题似乎很草率,甚至是懒惰。有人称之为聪明。我不知道该怎么想。银行可能有足够的资金来支付它,所以他们不妨忽视它,但这不是世界目前的运作方式。无论如何,我离题了。

从正确的立场来看,我们的读取存储具有强大的一致性保证,我们将以这样的方式编写我们的投影:如果余额被置于负数中,我们就不能将事务提交到读取存储。因此,可能发生的最糟糕的事情是从读取存储中扣除费用,但操作从未在事件存储中完全提交。在维护程序发现错误标志并修复帐户之前,用户将看到其帐户中缺少资金。我认为这是一个有效的妥协。

答案 2 :(得分:0)

这取决于“企业”想要发生什么。如果您希望客户端(命令的创建者)负责选择一个短URL,它应该有一个读取存储,它可以验证它的唯一性。当用户键入短URL时,视图应检查短URL是否唯一,如果不是,则显示验证错误。每当保存帖子时,事件将发布更新的信息(包括短URL),以使读取存储保持同步。