使用 Kafka 的事件源和每个实体类型的主题,外键会发生什么情况?

Event sourcing with Kafka and topic per entity type, what happens with foreign keys?

我正在尝试使用 Kafka 设计事件源系统。假设我们有一个非常简单的事件源应用程序,它包含四个聚合根:用户、类别、产品和购买。

根据 confluent 的一些指导,我创建了 4 个主题(为简单起见,我们假设每个主题有 1 个分区),每个聚合一个:

我的投影是 PostgresSQL 投影,它从 Kafka 读取事件,然后将每个事件变成一个 SQL 查询(INSERT、UPDATE 或 DELETE,具体取决于事件的类型)。

问题是数据库模型大概是这样的:

鉴于不能保证主题之间的事件顺序,如果我尝试在没有产品类别的情况下将产品添加到数据库,我很可能会收到来自数据库的 ForeignKey 错误。同样,如果我尝试在用户或产品尚未处理的情况下添加购买,我很可能会收到另一个 ForeignKey 错误。

我必须使用哪些分区选项来解决读取模型中的这个外键问题,并允许我的系统随着更多的实体和更多的事件而增长,而不必依赖将所有内容都放在一个主题中来避免外键问题?

我想到的是,虽然我可以独立处理用户和产品而不会发生冲突,但我仍然需要对购买做一些事情(以某种方式延迟对该主题的事件处理,直到确保用户和该产品存在于数据库中)。

如果您的目标数据模型使用外键,您基本上将不得不延迟成功插入,直到满足约束条件。

可以想象,要做到这一点(假设您 运行 同时使用流),是让查询在失败时重试直到成功,尽管这通常需要阻止来自受影响的消息的更多消息正在处理的分区。它还取决于事件的含义(例如,如果有可能发生删除或更新某些列),可能会打开由 non-deterministic 消费顺序引起的各种死锁和数据竞争。

考虑到外键约束之类的目的是验证和拒绝写入,它们在 read-model 中通常没有太大用处,其中写入已经被 [=18] 验证=]:将产品与类别相关联的事件应被视为产品和类别各自存在的无可辩驳的证据,无论 read-model 的状态如何。这至少表明以下之一:i) 从 read-model 中删除外键约束,ii) 让投影在 read-model 中创建最少的记录以允许外键验证,iii) 具有一些“待定”table 用于您知道存在但尚未完成的记录(您的投影可能会检查待定 table 以查看是否需要更新来完成待定记录) , 或 iv) 重新思考你的聚合。