Compiere (現 ADempiere/iDempiere 系列祖先) 採用全流程工作流驅動架構:所有業務單據 (訂單、發票、出貨、付款...) 皆由工作流引擎協調狀態流轉、任務分派、規則驗證。本文基於核心類圖與序列圖,深度解析其引擎設計精髓。
| 類別名稱 | 核心職責 | 關鍵屬性/方法 | 設計模式角色 |
|---|---|---|---|
| MWorkflow | 工作流定義容器:版本、啟用狀態、所屬模組 | AD_Workflow_ID, getProcess(), start() | Facade / Aggregate Root |
| MWFProcess | 流程圖定義:節點集合、轉移規則、起始/結束節點 | AD_WF_Process_ID, startWork(), getNodes() | Process Definition |
| MWFNode | 流程節點:類型(開始/結束/任務/決策/子流程)、XY 座標、關聯活動 | AD_WF_Node_ID, getActivity(), getNextConditions() | Node / State |
| MWFActivity | 工作項/任務實體:執行邏輯、指派規則、表單/程序/子流程 | AD_WF_Activity_ID, run(), performWork() | Command / Task |
| MWFNextCondition | 轉移條件:SQL 片段/規則引擎表達式、優先序、預設路徑 | Condition, SeqNo, evaluate() | Guard Condition |
| StateEngine | 狀態機核心:單據狀態定義、合法轉移矩陣、轉移動作攔截 | getNextState(), transition(), validate() | State Machine |
| DocAction (Interface) | 單據行為契約:prepare(), complete(), approve(), void(), close() | 所有單據 Model 實作此介面 | Strategy / Command |
| WMFAccess | 工作流存取控制:角色/用戶/組織對節點/活動的權限矩陣 | canAccess(), getAssignedUsers() | Authorization |
| WorkflowValidate | 部署時驗證:節點連通性、無孤立節點、條件完整性、循環檢測 | validate(), getErrors() | Validator |
// ProcessCtl.java
public static void startWorkflow(int AD_Workflow_ID, PO doc, Trx trx) {
MWorkflow workflow = new MWorkflow(Env.getCtx(), AD_Workflow_ID, trx);
workflow.start(doc, trx); // 傳入單據物件 (實作 DocAction)
}
// MWorkflow.java
public void start(PO doc, Trx trx) {
MWFProcess process = getProcess(); // 取得關聯流程定義
process.startWork(this, doc, trx); // 委派給流程實例
}
// MWFProcess.java
public void startWork(MWorkflow workflow, PO doc, Trx trx) {
// 1. 取得起始節點 (NodeType='S')
MWFNode startNode = getStartNode();
// 2. 取得起始節點關聯的活動
MWFActivity startActivity = startNode.getActivity();
// 3. 執行活動 — 這是同步阻塞呼叫
startActivity.run(workflow, doc, trx);
}
// MWFActivity.java
public void run(MWorkflow workflow, PO doc, Trx trx) {
// 1. 執行前驗證 (權限、前置條件)
if (!validate(workflow, doc, trx)) return;
// 2. 核心業務邏輯 — 由具體活動類型決定
performWork(workflow, doc, trx);
// 3. 執行後處理:評估下一條件、推進流程
completeWork(workflow, doc, trx);
}
protected void performWork(MWorkflow workflow, PO doc, Trx trx) {
switch (getWFAction()) {
case 'F': // Form 表單 - 等待用戶互動
createWorkItem(workflow, doc, trx);
break;
case 'P': // Process 程序 - 自動執行
executeProcess(workflow, doc, trx);
break;
case 'S': // Subflow 子流程
startSubWorkflow(workflow, doc, trx);
break;
case 'D': // Document 動作 - 委派單據 DocAction
docAction(doc, trx);
break;
}
}
private void completeWork(MWorkflow workflow, PO doc, Trx trx) {
// 1. 取得所有輸出轉移條件
List conditions = getNextConditions();
// 2. 按優先序評估,第一個滿足者勝出
for (MWFNextCondition cond : conditions) {
if (cond.evaluate(workflow, doc, trx)) {
MWFNode nextNode = cond.getNextNode();
MWFActivity nextActivity = nextNode.getActivity();
// 3. 遞迴執行下一活動 (同步鏈)
nextActivity.run(workflow, doc, trx);
return;
}
}
// 3. 無條件滿足 → 流程結束或錯誤
if (conditions.isEmpty())
workflow.setStatus("CO"); // Completed
else
workflow.setStatus("ER"); // Error - no path matched
}
所有業務單據 (Order, Invoice, Shipment, Payment...) 實作 DocAction:
public interface DocAction {
boolean prepare();
boolean complete();
boolean approve();
boolean voidIt();
boolean close();
String getDocStatus();
// ...
}
// Order.java
public class MOrder extends PO implements DocAction {
public boolean complete() {
// 1. 驗證明細、庫存、價格
// 2. 更新狀態 CO (Completed)
// 3. 觸發後續:建立出貨、發票、會計憑證
return true;
}
}
優勢:工作流引擎無需知曉具體單據類型,統一呼叫 docAction() 實現多態。
| 類型 | 代碼 | 執行語義 | 典型場景 |
|---|---|---|---|
| 表單任務 | F | 建立工作項 → 等待用戶操作 → 用戶完成 → 推進 | 審批、確認、輸入 |
| 自動程序 | P | 同步執行 Java 類/預存程序 → 立即推進 | 計算價格、生成單號、發送郵件 |
| 子流程 | S | 啟動另一工作流定義 → 等待子流程結束 → 推進 | 訂單流程包含信用檢核子流程 |
| 單據動作 | D | 呼叫單據 DocAction 方法 → 依結果推進 | 完成訂單、核准發票 |
MWFProcess) 定義節點與轉移拓撲StateEngine) 定義單據合法狀態與轉移規則// 節點層級權限
boolean canAccess(int AD_Role_ID, int AD_User_ID, int AD_Org_ID) {
// 1. 檢查角色/用戶是否在節點指派清單
// 2. 檢查組織存取權
// 3. 檢查單據層級敏感度
}
MWFActivity 新增 WFAction = 'R' (REST)performWork() 中 case 'R': callRestApi(); break;AD_WF_Activity 表新增 REST_Endpoint, REST_Payload_Template 欄位// MWFNextCondition 增加 Expression 欄位
// 評估引擎整合 GroovyShell
public boolean evaluate(MWorkflow wf, PO doc, Trx trx) {
Binding binding = new Binding();
binding.setVariable("doc", doc);
binding.setVariable("wf", wf);
binding.setVariable("ctx", Env.getCtx());
GroovyShell shell = new GroovyShell(binding);
return (Boolean) shell.evaluate(getExpression());
}
// 使用範例:表達式 = "doc.getTotalLines() > 100 && doc.getC_BPartner_ID() == 1000045"
WAITING_EXTERNALWorkflowCallback.complete(activityInstanceId, result)completeWork() 推進流程| 挑戰 | Compiere 方案 | 現代化改良建議 |
|---|---|---|
| 同步阻塞鏈 | 活動同步遞迴執行,長鏈導致交易超時 | 引入事件總線,活動發事件 → 背景 Worker 處理 → 回呼推進 |
| 資料庫鎖爭用 | 工作流實例、工作項、單據同一交易 | 樂觀鎖 + 重試、關鍵路徑拆分微服務 |
| 流程定義版本管理 | MWorkflow 版本欄位,運行中實例綁定舊版 | GitOps 風格:流程定義即代碼,CI/CD 部署、藍綠發布 |
| 監控與可觀測性 | 僅資料庫狀態欄位 | 整合 OpenTelemetry:TraceID 貫穿工作流實例、活動、單據 |
Compiere 工作流引擎以元資料驅動、介面契約解耦、狀態機雙重保障為核心設計哲學,實現了 ERP 複雜業務流程的標準化自動化。其類結構清晰展現了領域驅動設計 (DDD)的雛形:聚合根 (MWorkflow)、實體 (MWFProcess/Node/Activity)、值物件 (MWFNextCondition)、領域服務 (StateEngine/WorkflowValidate)、介面契約 (DocAction)。理解這套架構,不僅能駕馭 Compiere 系列產品二次開發,更能啟發現代微服務編排引擎、低代碼流程平台的設計思維。