Skip to content

Computation 计算型域 ​

算法不是状态,只存运行证据。


职责 ​

负责不负责
注册纯函数计算分布式执行
同输入复用结果算法血缘(只知道版本号)
版本变更自动失效结果过期(幂等键是永久的)
运行历史与耗时外部数据获取(应作为输入传入)

为什么不是存储 ​

定价、MRP、排程、成本分摊——这些是算法。真相是「输入 + 算法版本」,输出随时可重算。 把计算结果当真相存起来,就会腐烂成需要人工维护的数据。

所以这里存的是运行记录:

{ kind, version, inputHash, input, output, status, durationMs, actor, startedAt, finishedAt }

三条规则 ​

#规则实现
1同输入 + 同算法版本 ⇒ 复用结果{kind, version, inputHash} 唯一索引
2算法版本变更 ⇒ 自动失效旧缓存version 参与唯一键,无需清库
3失败不占幂等键部分索引只对 status='ok' 生效,失败可重试

第 3 条容易被忽略:如果失败也占唯一键,一次上游抖动会把该输入永久锁死。


纯函数约束 ​

typescript
computation.register({
  kind: 'pricing',
  version: 1,
  run: (input) => { /* 不要读库、不要调外部服务、不要看当前时间 */ },
});

需要外部数据时把它作为 input 传进来(调用方负责取数)。 违反这条会让「同输入同输出」不成立,缓存就会返回错误结果。


接口 ​

MethodPath说明
GET/api/computation/kinds已注册类型与版本
POST/api/computation/:kind/run执行(带缓存与幂等)
GET/api/computation/:kind/history运行历史

实际用途 ​

参考案例用它算订单金额:

typescript
const pricingRun = await computation.run('pricing', { lines, discountBps: 0 }, actor);
// 定价结果与案例金额不一致 → 直接报错,避免两处口径漂移

边界 ​

  • 无结果过期(要刷新就换 input 或加 force)
  • 无分布式执行(适合 CPU 轻量算法)
  • 无算法血缘(只知道 version,不知道两个 version 差在哪)

源码 ​

libs/modules/src/computation/
├── schemas/computation-run.schema.ts   运行记录 + 部分唯一索引
├── services/computation.service.ts     register / run / history
└── computation.controller.ts           3 个端点

Released under the MIT License.