Pipeline + Saga 分布式扩展规范(V1.0)
0. 架构结论先行(必须统一认知)
Pipeline 解决"单服务内的确定性执行顺序"
Saga 解决"跨服务副作用的一致性与补偿"
Pipeline ≠ Saga
Pipeline + Saga = 分布式可逆业务内核
1. 为什么 ERP 必须引入 Saga(而不是分布式事务)
1.1 ERP 的真实约束
-
跨服务(库存 / 财务 / 结算 / 物流)
-
高并发
-
外部系统不可控
-
允许"最终一致",不允许"状态错乱"
1.2 为什么不用 XA / 2PC
| 问题 | 结果 |
|---|---|
| 性能 | ❌ |
| 外部系统 | ❌ |
| 云原生 | ❌ |
| 可观测 | ❌ |
ERP 的一致性来自"可补偿",不是"强锁定"
2. 总体架构模型(升级后)
┌──────────────────────────┐
│ ScopedValue │
│ trace / tenant / operator │
└────────────▲─────────────┘
│
┌────────────┴─────────────┐
│ Local Pipeline │ ← 单服务内顺序 + 本地回滚
└────────────▲─────────────┘
│
┌────────────┴─────────────┐
│ Saga Orchestrator │ ← 跨服务编排
└────────────▲─────────────┘
│
┌────────────┴─────────────┐
│ Remote Service Pipelines │ ← 各服务独立 Pipeline
└──────────────────────────┘
3. 核心设计原则(强制)
3.1 单服务内:Pipeline
-
强类型 I → O
-
明确顺序
-
本地 RollbackAction
3.2 跨服务:Saga Step
-
明确 Forward / Compensate
-
允许异步
-
允许失败重试
4. Saga 的最小抽象模型(标准)
4.1 SagaStep 定义
public interface SagaStep<C> {
void forward(C context);
void compensate(C context);
}
4.2 SagaContext(跨服务业务上下文)
public record SagaContext(
String sagaId,
String docNo
) {}
约束:
-
只能放 全局业务标识
-
禁止放中间结果
-
禁止放服务私有对象
5. Pipeline × Saga 的结合点(关键)
5.1 每个 SagaStep 内部 = 一个 Local Pipeline
SagaStep
├─ Pipeline (本服务)
│ ├─ 校验
│ ├─ 执行
│ └─ 本地 rollbackActions
└─ compensate()
5.2 示例:库存服务 SagaStep
public class StockSagaStep implements SagaStep<SagaContext> {
public void forward(SagaContext ctx) {
executionPipeline.execute(
new ExecuteInput(ctx.docNo())
);
}
public void compensate(SagaContext ctx) {
rollbackPipeline.execute(
new RollbackInput(ctx.docNo())
);
}
}
⚠️ compensate 不是简单反调用
是独立设计的补偿语义
6. Saga Orchestrator(编排器)
6.1 顺序 Saga(ERP 最常见)
public class SagaOrchestrator {
private final List<SagaStep<SagaContext>> steps;
public void execute(SagaContext ctx) {
List<SagaStep<SagaContext>> executed = new ArrayList<>();
try {
for (SagaStep step : steps) {
step.forward(ctx);
executed.add(step);
}
} catch (Exception e) {
rollback(executed, ctx);
throw e;
}
}
private void rollback(
List<SagaStep<SagaContext>> steps,
SagaContext ctx
) {
for (int i = steps.size() - 1; i >= 0; i--) {
steps.get(i).compensate(ctx);
}
}
}
6.2 并行 Saga(可选进阶)
-
用于:
-
多仓库存冻结
-
多账户预扣
-
-
要求:
-
幂等
-
独立补偿
-
7. ERP 场景完整示例(销售出库)
销售出库 Saga
├─ 库存服务(扣库存)
├─ 财务服务(记应收)
├─ 物流服务(生成发货单)
失败场景:
-
物流失败
→ 回滚:财务冲销 → 库存回补
8. Saga + ScopedValue 的边界(再次强调)
ScopedValue 允许:
-
traceId
-
tenantId
-
operator
ScopedValue 禁止:
-
saga 执行状态
-
step 执行结果
-
是否已补偿
Saga 的状态必须持久化,而不是隐式存在
9. Saga 状态持久化(强烈推荐)
9.1 SagaInstance 表(示意)
| 字段 | 说明 |
|---|---|
| saga_id | 全局唯一 |
| doc_no | 单据号 |
| status | RUNNING / COMPENSATING / DONE |
| current_step | 当前步骤 |
| created_at | 创建时间 |
9.2 SagaStepInstance 表
| 字段 | 说明 |
|---|---|
| saga_id | 关联 |
| step_name | 库存 / 财务 |
| status | DONE / FAILED / COMPENSATED |
| retry_count | 重试次数 |
10. 失败与重试策略(ERP 必须)
| 场景 | 策略 |
|---|---|
| forward 失败 | 触发补偿 |
| compensate 失败 | 重试 + 人工介入 |
| 服务不可达 | 延迟重试 |
| 重复请求 | 幂等拒绝 |
11. 与三 Pipeline 的最终映射关系
| 层级 | 职责 |
|---|---|
| 审核 Pipeline | 是否允许启动 Saga |
| 执行 Pipeline | SagaStep.forward |
| 回滚 Pipeline | SagaStep.compensate |
12. 架构红线(必须写进规范)
🚫 禁止一个 SagaStep 内跨多个业务域
🚫 禁止 SagaContext 承载中间业务数据
🚫 禁止用消息最终一致掩盖不可回滚设计
13. 一句话架构总结(非常重要)
Pipeline 保证"一步不乱",
Saga 保证"走错能退"。
ERP 的核心竞争力,
来自"复杂业务的可逆性"。