【问题标题】:Uniqueness validation when using CQRS and Event sourcing使用 CQRS 和事件溯源时的唯一性验证
【发布时间】:2012-03-16 08:27:43
【问题描述】:

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

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

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

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

还有其他选择吗?请注意,我知道我的域可能不适合 cqrs 和 DDD,但我这样做是为了在一个小域中学习。

【问题讨论】:

标签: validation domain-driven-design cqrs


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我已阅读了有关此问题和相关问题的各种答案。

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

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

    例如,创建短 URL 的命令将验证读取存储是否已经包含这样的短 URL,并且我们只会提交我们的事件,如果我们可以先将更改提交到我们的读取存储。

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

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

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

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

    从正确性的角度来看,我们的读取存储具有强大的一致性保证,并且我们会以这样一种方式编写我们的投影,即如果余额为负数,我们就无法将事务提交到读取存储。因此,可能发生的最糟糕的事情是从读取存储中扣除费用,但操作从未完全提交到事件存储中。在维护程序注意到错误标志并修复帐户之前,用户会看到他们的帐户中缺少钱。我认为这是一个可行的妥协。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 这就是我的意思“我知道在我发送命令之前我会通过从我的读取存储中读取来检查它在创建创建帖子命令或更新帖子时是否有效命令。”但是,当涉及到域时,不能保证它仍然是唯一的。这在创建博客文章时不太可能发生,但我认为这是一个有趣的场景,我仍然需要弄清楚在使用 CQRS 时如何解决。我的意思是如何处理域中的检查?
      • 在我正在处理的系统中,数据存储中有一个唯一约束,因此如果发生两个同时保存具有冲突字段的不同实体的边缘情况,该约束会导致异常冒泡到可以手动修复的 UI。除非领域逻辑可以纠正这种情况,否则必须由人来处理。
      猜你喜欢
      • 2012-03-18
      • 2019-02-03
      • 2019-01-31
      • 1970-01-01
      • 1970-01-01
      • 2016-02-20
      • 2018-11-15
      • 2019-09-22
      • 2018-04-13
      相关资源
      最近更新 更多