在 CQRS 和事件驱动架构中,读取端生成的状态更改是否应该创建更多事件?
In CQRS and Event Driven Architecture, Should State Changes generated at the read side create more events?
假设我正在创建一个以事件为源并使用 CQRS 的投标网站。
这是我们的两个基本实体:
public class Auction {
int Id {get; set;}
//Active, Closed
AuctionStatus Status {get; set;}
Bid? HighestBid {get; set;}
double StartingBid {get; set;}
}
public class Bid{
int Id {get; set;}
double Value {get; set;}
//Accepted, Rejected, Created
BidState State {get; set;}
}
我的命令端有 2 个命令:
- 创建拍卖
- 出价
命令端简单地验证拍卖是否存在以及出价金额是否在可接受的范围内,然后发出一个事件以供查询端处理。
当我们创建拍卖时,它会以 'Active' 状态
保存到聚合数据库
当我们出价时,它会进入 'Created' 状态的读取端,然后可以根据聚合数据的当前状态更新其状态。
例如:
- 创建了一个起始价格为 1200 美元的拍卖
- 出价为 1100 美元,最初处于 'Created' 状态
- 出价是 'Rejected',原因是 'Outbid'
为了使拒绝此投标更易于审核,我们将 'BidStateChange' 保存到聚合数据库,其中只包含 BidId、原因代码和新状态:
public class BidStateChange {
int BidId {get; set;}
BidState newBidState {get; set;}
string Reason {get; set;}
}
首先我们将 Bid 的状态更新为 Rejected,然后我们保存 BidStateChange
我的问题是,这种状态变化是否应该向 Event Steam 发出事件?
我倾向于不将状态更改发布到事件流,否则我们将在重播时复制事件。考虑一下:
假设我们确实为 BidStateChange 发出一个事件,我们重播以下事件:
1. Create Auction -> 2. Low Bid Placed -> 3. Bid State Changed to Reject
当我们重放这些事件时,不会导致 'Reject State Change' 被创建两次吗?第一次作为事件 2 的结果,然后在事件 3 中再次出现。因为我们正在重播我们所有的事件历史?
也许我想多了 'replayability' 也许提醒出价状态已更改更有意义,以防其他微服务需要知道任何出价的当前状态。
任何对此架构有更多经验的人可以提供建议吗?谢谢!
Should State Changes generated at the read side create more events?
“在读取端生成的状态更改”是一个非常……奇怪的……用于 CQRS 模式的拼写。
在通常的安排中,来自“外部”的信息被传递到写入模型,这是我们执行计算以确定实体的新“权威”状态是什么的地方。
读取模型是状态的非权威副本;在过去的某个时间点是准确的,但不一定是最新的。想想“缓存”。
缓存产生的状态变化很奇怪,因为缓存是写入模型的反映。
现在,有时会发生的情况是,我们有两个模型,第二个模型会对第一个模型发生的变化感兴趣。
在那种情况下,我们使用某种形式的管道将一个模型的输出连接到另一个模型的输入。
所以我们让爱丽丝做她的工作,爱丽丝的工作生成的报告之一(读取模型)成为鲍勃工作时的输入之一。
现在,作为优化问题,您可以在缓存更新时广播“事件”,以便订阅者可以知道有可供处理的信息。但是您可能不应该将这些定义为“状态变化”,即对域模型中的实体进行更改。在写模型的治理下,这些变化已经发生。
假设我正在创建一个以事件为源并使用 CQRS 的投标网站。
这是我们的两个基本实体:
public class Auction {
int Id {get; set;}
//Active, Closed
AuctionStatus Status {get; set;}
Bid? HighestBid {get; set;}
double StartingBid {get; set;}
}
public class Bid{
int Id {get; set;}
double Value {get; set;}
//Accepted, Rejected, Created
BidState State {get; set;}
}
我的命令端有 2 个命令:
- 创建拍卖
- 出价
命令端简单地验证拍卖是否存在以及出价金额是否在可接受的范围内,然后发出一个事件以供查询端处理。
当我们创建拍卖时,它会以 'Active' 状态
保存到聚合数据库当我们出价时,它会进入 'Created' 状态的读取端,然后可以根据聚合数据的当前状态更新其状态。
例如:
- 创建了一个起始价格为 1200 美元的拍卖
- 出价为 1100 美元,最初处于 'Created' 状态
- 出价是 'Rejected',原因是 'Outbid'
为了使拒绝此投标更易于审核,我们将 'BidStateChange' 保存到聚合数据库,其中只包含 BidId、原因代码和新状态:
public class BidStateChange {
int BidId {get; set;}
BidState newBidState {get; set;}
string Reason {get; set;}
}
首先我们将 Bid 的状态更新为 Rejected,然后我们保存 BidStateChange
我的问题是,这种状态变化是否应该向 Event Steam 发出事件?
我倾向于不将状态更改发布到事件流,否则我们将在重播时复制事件。考虑一下:
假设我们确实为 BidStateChange 发出一个事件,我们重播以下事件:
1. Create Auction -> 2. Low Bid Placed -> 3. Bid State Changed to Reject
当我们重放这些事件时,不会导致 'Reject State Change' 被创建两次吗?第一次作为事件 2 的结果,然后在事件 3 中再次出现。因为我们正在重播我们所有的事件历史?
也许我想多了 'replayability' 也许提醒出价状态已更改更有意义,以防其他微服务需要知道任何出价的当前状态。
任何对此架构有更多经验的人可以提供建议吗?谢谢!
Should State Changes generated at the read side create more events?
“在读取端生成的状态更改”是一个非常……奇怪的……用于 CQRS 模式的拼写。
在通常的安排中,来自“外部”的信息被传递到写入模型,这是我们执行计算以确定实体的新“权威”状态是什么的地方。
读取模型是状态的非权威副本;在过去的某个时间点是准确的,但不一定是最新的。想想“缓存”。
缓存产生的状态变化很奇怪,因为缓存是写入模型的反映。
现在,有时会发生的情况是,我们有两个模型,第二个模型会对第一个模型发生的变化感兴趣。
在那种情况下,我们使用某种形式的管道将一个模型的输出连接到另一个模型的输入。
所以我们让爱丽丝做她的工作,爱丽丝的工作生成的报告之一(读取模型)成为鲍勃工作时的输入之一。
现在,作为优化问题,您可以在缓存更新时广播“事件”,以便订阅者可以知道有可供处理的信息。但是您可能不应该将这些定义为“状态变化”,即对域模型中的实体进行更改。在写模型的治理下,这些变化已经发生。