模組化與復用
sub_workflow、把一段邏輯封裝成模組、Promote / Demote 一鍵重構 —— 讓工作流程不再一則複製貼上。
當你做到第二、第三條工作流時,會發現某段邏輯反覆出現 —— 拉 ASA 數據、發 Telegram 消息、生成 Excel 報表。再複製粘貼下去,改一處要改三處、測試重複、版本失控。這時候就該做模塊化了。
模板 = 起始文檔的快照,克隆後獨立演化;模塊 = 可被引用的子流程,升級後調用方自動受益。兩者不是替代關係,是互補的。本頁講模塊。
什麼是「模塊」
一個"模塊"本質上就是一個聲明瞭 WorkflowModuleContract 頂層字段的 workflow。它額外公開了:
- inputs — 調用方必須傳入的參數(帶名稱、類型、必填、默認值、描述)
- outputs — 執行完產生的結構化結果(調用方可直接引用)
- version — 語義化版本號,便於調用方鎖定
name: my-team-slack-notifier
version: "1.0.0"
module:
enabled: true
inputs:
webhook_url:
type: string
required: true
description: Slack incoming webhook URL
channel:
type: string
required: false
default: "#general"
message:
type: string
required: true
mentions:
type: array
items: { type: string }
required: false
outputs:
message_ts:
type: string
description: Slack 返回的 message timestamp,可用于后续更新消息
status:
type: string
enum: [sent, failed]
# 模块自己的步骤(内部细节,对调用方黑盒)
steps:
- id: format_text
type: code
language: node
code: |
const msg = process.env.WF_VAR_MESSAGE;
const mentions = JSON.parse(process.env.WF_VAR_MENTIONS || '[]');
return { text: mentions.map(u => `<@${u}>`).join(' ') + ' ' + msg };
- id: post
type: code
depends_on: [format_text]
credentials: [slack_oauth]
code: |
// 调 Slack API,返回 message_ts / status …有了契約之後,調用方(父 workflow)的 sub_workflow step 會:
- 在保存時靜態校驗 —— 必填 inputs 缺一項就不讓保存;
- 在運行時校驗 —— 類型不符 / 枚舉不在列表裡,直接報錯;
- 在編輯器裡自動補全 —— 你寫 inputs: 時能下拉看到模塊公開了哪些字段。
父工作流如何調用模塊
steps:
- id: build_report
type: code
# ...
- id: notify
type: sub_workflow
module: my-team-slack-notifier # 引用我们上面定义的模块
version: "^1.0.0" # 锁主版本
inputs:
webhook_url: "{{credentials.slack_webhook}}"
channel: "#ops-alerts"
message: "日报已生成:{{steps.build_report.data.summary}}"
mentions: ["U1234", "U5678"]
- id: record
type: code
depends_on: [notify]
code: |
const ts = process.env.STEP_OUTPUT_NOTIFY_MESSAGE_TS;
// 把 ts 存到你自己的数据库,方便后续更新那条消息 ...要點:
module: dingyue-module-slack-deliver是模塊 id,平臺會按名字 + version 解析。inputs:和模塊契約裡的 inputs 一一對應。- 調用完成後,可通過
{{steps.deliver.outputs.message_id}}讀模塊輸出。
讓已有工作流變成模塊:Promote to module
大部分時候你不會從零寫一個模塊,而是從現有工作流裡抽。流程:
- 打開一個你覺得"這段邏輯值得複用"的工作流。
- 在畫布裡用鼠標框選或 Shift+點擊選中若干連續 step。
- 右鍵菜單 →「Promote to module」,或頂部工具欄同名按鈕。
- 彈窗裡:
- 起一個有意義的模塊名字(全局唯一,建議前綴你團隊名,例:my-team-slack-notifier);
- 平臺會自動根據選中片段推斷 inputs / outputs 初稿,你可以編輯;
- 確認後點「創建模塊」。
- 原工作流裡這幾步會被替換成一個 sub_workflow 節點 —— 參數和連線自動對齊。
就像 IDE 裡的"提取函數"重構。你選一段代碼,IDE 幫你抽成一個帶參數和返回值的函數,並把原位置替換成調用。Braidrun 做的是完全一樣的事 —— 只不過對象是 workflow step。
模塊升級與版本兼容
每次保存一個模塊都會創建一個新版本。父 workflow 在 sub_workflow step 裡可以選擇:
version: latest— 自動用最新版(適合你自己維護的模塊)version: "1.2.0"— 鎖定到具體版本(適合引用別人的模塊、怕升級破壞兼容)version: "^1.2.0"— 允許小版本升級,禁止跨大版本
什麼是"兼容的變更"?
- ✓ 加一個 optional input(必填 / 默認值都不算破壞)
- ✓ 加一個 output 字段
- ✗ 刪 / 改名 input
- ✗ 刪 / 改名 output
- ✗ 改 input 類型
做破壞兼容的改動時,語義版本應該升到下一個 MAJOR。平臺不會強制,但調用方鎖 ^X.Y.Z 時會受保護。
環路檢測
模塊 A 不能間接引用自己。平臺在兩層做了檢測:
- 發佈時靜態檢測 — 保存模塊時,DFS 掃所有它引用的其它模塊的依賴圖,有環則拒絕保存。
- 運行時動態檢測 — 執行期間如果出現 runtime 解析到的環(譬如條件分支後才被引用),會立刻拋錯。
最大遞歸深度默認 8 層。這是"父調子調孫"的深度,不是單次調用的 fanout。
變量隔離
模塊跑起來是一個"子 WorkflowExecutionContext",和父上下文完全隔離:
- 父的 variables / steps outputs / agents 默認不繼承給子;
- 子的內部 step 輸出也不會溢出到父 —— 父只能看到契約裡聲明的 outputs;
- 跨父子傳數據的唯一方式是 inputs / outputs(顯式契約)。
這種隔離是模塊化可靠性的基石 —— "黑盒"不是比喻,是字面意義上的。
Demote from module:把模塊還原為內聯 step
如果你改著改著發現某段邏輯其實只有一處在用,做成模塊反而增加了理解成本,可以反向 Demote:
- 在父 workflow 裡選中那個 sub_workflow 節點;
- 右鍵 →「Demote inline」;
- 平臺把模塊的 step 展開回父 workflow,sub_workflow 節點消失。
原模塊本身不會被刪除 —— 如果還有其它工作流引用它,那些仍然有效。
在哪裡管理所有模塊
左側主導航「模塊庫」是你的總管視圖。這裡可以:
- 看所有模塊列表(自帶的 12 個 + 你團隊創建的)
- 按 "誰在引用我" 反向查詢 —— 改模塊前先看誰會受影響
- 看模塊的版本歷史 / diff
- 發佈 / 歸檔 / 轉移所有權
什麼時候該做模塊
- ✓ 同一段邏輯在 2 個以上 workflow 裡出現過
- ✓ 一段邏輯穩定且對外有清晰契約(典型的 "發消息 / 查報表 / 生成 Markdown")
- ✓ 一個單 workflow 已經超過 15 個 step,可讀性下降
- ✗ 一段邏輯只用一次且未來不會複用 —— 直接內聯
- ✗ 一段 2–3 行的小工具函數 —— 用 code step 的代碼複用比模塊更自然