如果管道的形状不是框架决定的,而是你自己声明的

1 阅读4分钟

你有没有遇到过这种情况:改了一个中间件的位置,另一个中间件出了问题——不是因为逻辑错了,而是因为它隐式依赖了前者的执行顺序?

或者更常见的:你需要在某个管道阶段前面插一步处理,但框架没给你这个位置,你只能把逻辑塞进最近的一个阶段里,然后写注释提醒后人"这段代码实际上不属于这里"。

后端框架都给你一条管道。你在里面写逻辑,但管道本身的形状——有几个阶段、阶段之间什么顺序、每个阶段怎么执行——是框架写死的。覆盖你的场景就很舒服;覆盖不了,你就得绕。

如果管道的形状本身是你定义的呢?


可编程的管道拓扑

Kernel 做的事情是:管道有几个阶段、叫什么名字、按什么顺序执行、每个阶段用什么执行模型——全部由你声明。

Kernel 里,管道的每个阶段叫做 Slot。你可以声明任意数量的 Slot,放在三个区域的任何一个:INGRESS(请求进来时)、PROCESS(核心处理)、EGRESS(响应出去后)。

Slot 之间的顺序不是数组下标,而是通过锚点声明拓扑关系——"我在 X 之后、Y 之前"。编译时自动排序。你不需要关心全局顺序,只需要声明每个阶段和谁相邻。加一个新阶段,不会打破已有阶段的顺序。

每个 Slot 内部的执行方式也是可选的:洋葱模型、线性、或者自定义。

比如,声明一个自定义的管道阶段,插在已有阶段之间:

// 声明一个叫 "cors" 的 Slot,用洋葱模型执行
// 锚点:在 catch 之后、receive 之前
const Cors = Kernel.ingress('cors', {
  composer: 'onion',
  anchors: { after: ['catch'], before: ['receive'] },
});

你不需要知道别的 Slot 在哪里。只需要声明"我和谁相邻",Kernel 在编译时自动把所有 Slot 排成正确的拓扑顺序。

这意味着你不是在用某个框架的管道,而是在定义自己的管道

@hwy-fm/std 就是一个例子——它用 Kernel 搭出了一个 8 阶段的管道框架(Catch → Receive → Guard → Process → Deliver → Trace……)。但这 8 个阶段不是 Kernel 的功能,而是用 Kernel 的原语声明出来的一种管道形状。你可以声明一个完全不同的形状,三个阶段或三十个阶段,由你决定。

分形:每个层级结构一致

管道可以自定义,这还不是最有意思的部分。

Kernel 的每个实例都是独立的计算内核——自己的 Slot 拓扑、自己的路由表、自己的编译产物、自己的依赖注入树。而一个 Kernel 的管道步骤可以桥接到另一个 Kernel,触发它的完整管道。

              System
       ┌────────┼────────┐
       │        │        │
     Auth    Business    View
    ┌──┼──┐  ┌──┼──┐   ┌──┼──┐
    I  P  E  I  P  E   I  P  E

System 是一个 Kernel。Auth、Business、View 也各是一个 Kernel。它们有不同的管道形状:

const Auth     = KernelModel();
const Business = KernelModel();

// Auth 的管道:三个阶段
@Auth.slot({ name: 'identify', stage: 'INGRESS' })
@Auth.slot({ name: 'verify',   stage: 'PROCESS' })
@Auth.slot({ name: 'session',  stage: 'EGRESS' })

// Business 的管道:完全不同的三个阶段
@Business.slot({ name: 'validate', stage: 'INGRESS' })
@Business.slot({ name: 'execute',  stage: 'PROCESS' })
@Business.slot({ name: 'event',    stage: 'EGRESS' })

每个 Kernel 的接口完全一致——createContext()dispatch()。组合方式和粒度由你决定。

这种结构是自相似的:一个 Kernel 看起来和它的子 Kernel、子子 Kernel 结构一样。传统框架应对复杂性的方式是加更多中间件。Kernel 的方式是加更多 Kernel。

复杂性不是通过堆代码来管理的,而是通过结构隔离来管理的。认证出了问题?只影响 Auth Kernel。Business Kernel 的管道、路由、状态完全不受影响。团队可以独立开发、独立测试各自的 Kernel,就像独立的服务——但跑在同一个进程里,没有网络开销。

这就是分形的意义:不管系统多复杂,每个局部看起来都和整体一样简单。

试一下

多协议同指令 — HTTP 和 WebSocket 走同一条管道,同一套认证和业务逻辑:

npm install -g @hwy-fm/cli
hwy create my-api --template api
cd my-api && npm install && npm start
# → http://localhost:4000  左边测 HTTP,右边测 WebSocket,同一套管道

image.png

多平台同代码 — 浏览器 SSR、终端 CLI、HTTP API,共享同一套业务逻辑:

npm install -g @hwy-fm/cli
hwy create my-app --template advanced
cd my-app && npm install && npm start
# → 浏览器、终端、API 三个出口,业务代码只写一份

截屏2026-03-15 22.35.20.png 两个模板展示了 Kernel 的两个维度:一个是协议无关,一个是平台无关。管道和业务逻辑只写一份,入口和出口各自处理。

源码只有十几个文件,但每一个都值得看——它展示了一种你可能没见过的后端组织方式。


@hwy-fm/kernel · @hwy-fm/std · hwy-templates