我正在尝试使用Event Sourcing实现我自己的CQRS基础架构,以便更好地学习它。作为一个示例项目,我正在实现一个博客引擎,我知道它可能不是一个完美的契合,但我只想做一些真实的事情。
我现在遇到的问题是验证。每个帖子都有shortUrl
,shortUrl
应该是唯一的,但我应该在哪里将此验证放在域中?我知道在我通过读取读取存储器发送命令之前我将进行验证,以检查在创建create post命令或更新post命令时它是否有效。
我可以想到两个“解决方案”。
Blog
聚合,用于跟踪所有与博客相关的设置以及对所有帖子的引用。但是在我看来这个问题是我必须在那个场景中处理聚合之间的通信,以及每当我需要验证shortUrl
的唯一性时,我需要读取从事件存储到事件存储的所有事件。创建所有帖子,这似乎很复杂。还有其他选择吗?请注意,我知道我的域名可能不适合cqrs和DDD,但我这样做是为了在小域中学习。
答案 0 :(得分:2)
我会选择一个只负责生成独特ShortURL的域名服务。您可以使用事务DB来实现此行为。通常,此服务将由BlogPost聚合的命令处理部分使用。如果存在重复的ShortURL,则可以触发DuplicateUrlErrorEvent。 您可以通过使用相同的数据源创建精简查询模型来预先在UI中捕获这个(但绝不是100%),这样您就可以在提交帖子之前查询缩短的URL是否唯一(如@ RyanR的回答所述) )。
答案 1 :(得分:1)
我已经阅读了关于这个和相关问题的各种答案。
决定归结为正确性。如果您可以宽容并接受某种程度的操作的不完美行为,那么您的问题就更容易解决,特别是在弱一致性保证下。
但是,如果您想要一致性,则应使用具有强一致性保证的持久性服务。
例如,创建短URL的命令将验证读取存储不包含这么短的URL,如果我们可以先将更改提交到读取存储,我们将只提交事件。
如果我们可以将更改提交到读取存储,我们没有违反任何唯一性约束(假设您的读取存储实施了这样的约束),然后我们就可以继续了。
但是,由于我们有两个事务不一定在同一个数据库上,所以在第一次提交后我们可能会失败。这没关系,因为整个操作也会失败。读存储将反映一段时间内的不一致状态,但只要我们修复聚合,读存储就会恢复到一致状态。
作为维护程序,我们可以定期修复可能存在错误的聚合。您可以通过引入错误标志来实现此目的,只有在两个事务成功提交时才会清除该错误标志。
有一个例子,银行会允许用户透支他们的帐户,因为他们有附加费用来补偿。这引发了一些问题,因为解决这样的问题似乎很草率,甚至是懒惰。有人称之为聪明。我不知道该怎么想。银行可能有足够的资金来支付它,所以他们不妨忽视它,但这不是世界目前的运作方式。无论如何,我离题了。
从正确的立场来看,我们的读取存储具有强大的一致性保证,我们将以这样的方式编写我们的投影:如果余额被置于负数中,我们就不能将事务提交到读取存储。因此,可能发生的最糟糕的事情是从读取存储中扣除费用,但操作从未在事件存储中完全提交。在维护程序发现错误标志并修复帐户之前,用户将看到其帐户中缺少资金。我认为这是一个有效的妥协。
答案 2 :(得分:0)
这取决于“企业”想要发生什么。如果您希望客户端(命令的创建者)负责选择一个短URL,它应该有一个读取存储,它可以验证它的唯一性。当用户键入短URL时,视图应检查短URL是否唯一,如果不是,则显示验证错误。每当保存帖子时,事件将发布更新的信息(包括短URL),以使读取存储保持同步。