跳转到主要内容

CQRS 与 Saga:复杂业务的读写分离与最终一致

读写模型分家之后,复杂长事务如何用 Saga 串起来,又如何在不牺牲对账能力的前提下接受最终一致。

很多团队把 CQRS 当成「加个 MQ 同步数据」就完事,结果读模型对了、写模型乱了,长事务更无从下手。CQRS 解决「读写诉求不同」,Saga 解决「业务跨多个聚合却不能开大事务」,两者常一起出现但职责不同。

读写为什么要分家

写模型关心一致性与不变量,是一组小而强的聚合;读模型关心展示与组装,最好能直接查出前端要的 DTO。诉求冲突时强行共用一个模型,两边都不讨好。

account.withdraw(money);     // 写侧:只做业务
repository.save(account);
findByCardNo(cardNo);        // 读侧直接投影无需聚合

不要为了 CQRS 而 CQRS。单表就能满足读写、流量不大的模块,分家只会制造延迟和负担。

Saga 管理长事务

转账、跨境汇款天然跨账户跨系统,不可能用本地事务锁住。Saga 把大事务拆成一串本地事务,每步都有补偿。

模式思路适用
编排中心协调器逐步调用并补偿需强管控
协同各服务靠事件触发去中心低耦合

补偿与幂等

Saga 最怕「补偿自己也失败」,所以每步都要幂等:用业务流水号去重,重试不重复扣款。每步记录状态机 PENDING -> DONE / COMPENSATED,补偿顺序与正向相反,对账做兜底而非第一道防线。

最终一致不是「不管了」,而是把一致性从「即时」换成「可验证」:允许短暂中间态,但必须能收敛到正确态。

落地上,CQRS 让读写各取所需,Saga 让长流程可回退,再加独立对账,复杂业务才算立得住。