首页 文章

CQRS /事件采购 - 瞬态事件

提问于
浏览
0

在开发我的应用程序期间,我发现我需要发出一些实际上不会修改聚合状态的事件,但是为了更新读取模型(瞬态事件?)需要它们 . 例如如果在我的代码(域模型)中,我在层中保持数字层次结构的状态,如:

1    4    7
     5    8
3    9

并且读取模型正在进行事件的投影,例如(从左到右的顶部数字):

1
5
3

然后,当我在聚合根 RemovedNumber(1) 中触发事件时,如果这是我触发的唯一事件(因为它足以更新聚合状态),读模型将不知道它需要用4替换数字1 .

? <--- SHOULD BE 4 SINCE 4 IS UNDER 1
5
3

所以基本上,我需要另外触发: NowShowNumber(4 instead of 1) ,然后读取模型将知道项目:

4
5
3

事件 RemovedNumber(1) 应保存在事件存储中,因为它会影响聚合的内部状态 . 事件 NowShowNumber(4 instead of 1) 也应该存储在事件存储中,因为它影响读取模型(并且应该在重新投影时重放),但是在从事件流重构聚合根时可能不应该使用它 .

这是CQRS /事件采购系统的标准做法吗?有替代解决方案吗?

4 回答

  • 0

    为什么 Read model 不知道显示数字 4 ?在 AddNumber(1) 之前,Aggregate没有发出 AddNumber(4) 吗?然后 Read model 在他的部分复制了必要的状态,基本上是一个带有数字的堆栈,以便拉出前一个数字并显示它 .

    在CQRS中,为了帮助 Read models ,当状态发生变化并发出 Event 时, Aggregate 还包括 Event 中先前状态的位 .

    在您的情况下,发出的 Event 可以具有以下签名 RemovedNumber( theRemovedNumber, theNewCurrentNumber) ,特别是 RemovedNumber(1, 4) .

  • 1

    我将这些事件称为 out of band 事件并将它们保存到与我用水合聚合物不同的流中 . Haven 't heard anyone else doing it but haven'听到了任何不好的论据 - 特别是如果你有一个合法的案例来发布对集合没有任何影响的事件 .

    在你的情况下,如果我理解你的问题,我会让域写一个 TopLevelNumberChanged 事件,读模型将看到和处理 . 显然,在保湿时它不会读取该事件 .

  • 1

    我无法看到,如果事件不影响您的预测变化,那就完全是个问题 . 根据投影,投影可能会忽略许多事件 .

    话虽这么说,如果你说这两个事件是相辅相成的,你可能需要再看看设计/意图 . 你怎么知道拨打第二个命令?单个命令是不是可以做到这一点?该事件可以返回完整的更改:

    NumberReplacedEvent ReplaceNumber(1, 4);
    

    该事件将包含所有州:

    public class NumberReplacedEvent
    {
        int ReplacedNumber { get; set; }
        int WithNumber { get; set;
    }
    
  • 0

    根据我的理解,没有一个正确的答案 . CQRS / Event Sourcing只是一种帮助您建模数据流的工具 . 但它仍然是 your 数据,您的业务规则,您的用例 . 换句话说:其他一些公司可以使用完全相同的数据模型,但具有不同的事件结构,因为它更适合其用例 .

    一些例子:

    我们假设我们有一个在线商店 . 每次客户购买产品时,我们都会降低该产品的 inStock 值 . 如果客户发回产品,我们会增加 Value .

    命令很简单: BuyProduct(id: "123", amount: 4)

    对于结果事件,我们有(至少)2个选项:

    • ProductBuyed(id: "123", amount: 4) (delta值)

    • ProductBuyed(id: "123", newInStockValue: 996) (新总值)

    (你也可以发布4次简单的 ProductBuyed(id: "123") 事件)

    或者您可以同时拥有多个结果事件:

    • ProductBuyed(id: "123", amount: 4)

    • InStockValueForProductChanged(id: "123", newValue: 996)

    在线商店可能会有多个对这些事件感兴趣的阅读模型 . 产品页面只想显示996件商品! Shop Statistics页面想要显示今天售出的4件商品 . 虽然两个选项(总和delta)都很有用 .

    But 如果两个事件中只有一个,则两个页面都可以工作 . 然后读取方必须进行计算: oldTotal - newTotal = deltaoldTotal - delta = newTotal .

    There are even more 可能的解决方案 . 例如:

    • Checkout Service发布 ProductBuyed(id: "123", amount: 4) 事件

    • Stock Service收到此事件,减少库存,然后发布 InStockValueForProductChanged(id: "123", newValue: 996) 事件

    这实际上取决于您的业务需求 .

    我的建议:

    • I prefer 当写模型只负责管理时商业规则 . 获取命令,验证它,发布看起来与命令内容非常相似的事件 .

    • And 读取模型也应该尽可能简单 . 获取活动,更新模型 .

    • 如果必须进行计算,可以选择以下几种方法:

    • 计算是业务规则的一部分?然后你的写作方必须计算结果 . 在这种情况下,您已经编写了算法,CPU已完成其工作,并且您可以免费获得结果值 . (只需将结果包含在已发布的事件中)

    • 计算非常复杂和/或有多个事件消费者需要结果 . 然后,最好一次计算并将结果包含在事件中,而不是为每个涉及的事件使用者计算n次 . 复杂可能意味着:

    • 花了很多时间

    • 非常CPU /内存密集型

    • 需要特殊/巨大的外部库(想象你必须在每个读取服务中包含一些图像处理库)

    • 计算是许多不同事件组合的结果(即它变得复杂):构建一个负责计算的外部服务 . 这样,您可以通过提供此服务的多个实例轻松扩展 .

    • 如果计算是 not 业务规则的一部分 and 它只是 and 只有 a single 服务需要结果 or 如果它与读取模型相关 only :将其放在读取端 .

    最后这是一个权衡:

    • 重复算法?您可以使用不同的编程语言编写多个事件使用者 . 您想多次实施该算法吗?

    • 更多网络流量/更大的活动商店?如果将计算结果包含在事件中,则需要在服务之间存储和传输更多数据 . 您的基础设施可以处理吗?

    • 您的写入/读取服务是否可以承担额外负载?

相关问题