FR-034 — jedi-flow-engine vs BE WorkflowExecutionService 收斂 feasibility

項目 內容
日期 2026-06-08
性質 Feasibility 評估(非 bug、無 live bug)
結論 採方案 c:文件化現狀,不做合併。目前無第二個稽核產品,合併收益收不到、成本高
來源 套件重刻漂移收斂案例2;詳細 method 對照見 docs/analysis/2026-06-07-flow-engine-package-vs-be-divergence.md

1. 問題

BE app/flow_engine/service/workflow_execution_service.py(1615 行)把 WorkflowExecutionService 重寫成產品專屬超集,與 jedi-flow-engine 套件版(598 行)各自演化。要不要把兩份收斂成一份?

2. 現況差異(2026-06-08 重讀)

結構:BE 是嚴格超集 —— 13 個同名 method + 17 個 BE 獨有 + 套件獨有 0。

  • 9 個薄 CRUD getter:兩邊精神一致(BE 多 locale/user/RLS)。
  • 3 個熱區complete_job / revert_job / start_workflow_execution):核心 BPMN 邏輯相同,但 BE 把產品副作用「織」進控制流。以 complete_job 為例(套件 112 行 → BE 171 行),同一骨架插了 9 個產品副作用點:權限守門(C16)、GrcErrorCode、AP-task 通知、專案狀態、I7 CANCEL-aware 判定、AP-task 狀態、job_execution_uid 注入(巢狀子流程)、survey 關聯、todo 通知。revert_job 更甚(86 → 221 行)。
  • 17 個 BE 獨有 methodassert_project_participant / complete_main_workflow_job / notify_* / process_survey_property / update_assessment_plan_task_job_link / build_task_combinations 等,全依賴 BE 專屬 domain(AP / Project / Participant / Notification / Survey / Drive),無法進通用套件。

3. 關鍵 reframe —— 「重用」不是 yes/no,要看重用哪一層

現況其實已經分兩層:

在哪 重用性
通用 BPMN 引擎 jedi-flow-engine 套件(598 行) 已可重用——不同領域的產品今天就能拿去用
GRC/稽核編排(17 method + 9 織入副作用) BE 主專案 綁主專案;只有「另一個稽核產品」會想共用

「很多是這個產品的核心、寫在主專案」本身是對的(DDD:產品專屬編排該在產品端,不是技術債)。

未來若出現第二個產品:

  • 不同領域 → 用套件版通用引擎即可,不用做任何事
  • 另一個稽核/合規產品 → 正確做法是抽第二個套件層 jedi-audit-workflow(疊在 flow-engine 之上、裝稽核編排,產品專屬部分 adapter 注入,mirror FR-032 jedi_information_system 抽法)——不是把東西塞進通用的 jedi-flow-engine(會污染通用層)。

兩條路(hook 化 / 抽 audit 層)都得先把副作用從控制流裡 un-weave,這才是真成本,且只有在真有第二個稽核產品時才划算。

4. 考慮過的選項

方案 內容 評估
a 對齊現況 套件版 5 項修正已是 BE 行為的可攜等價物 已完成,無動作
b 完整合併 BE 拆薄層 + 套件補 lifecycle hook + 巢狀 sub-process 不採:去重收益僅 ~250~300 行共用骨架;17 個 BE method + 9 織入副作用合併後仍留 BE;要動稽核引擎核心 + 全回歸;無 live bug、無第二 consumer
c 文件化現狀 承認刻意雙實作,文件化邊界 採用:低風險;本文件即現狀紀錄
(未來)抽 audit 層 若有第二個稽核產品才做 defer,trigger 見 §6

5. 架構合規檢查(2026-06-08)

對 BE WorkflowExecutionService 查 DDD 分層 / transaction / error handling:

檢查項 結果
app 層碰 infra session / ORM(get_session / session.query / self.session ✅ 無
直接 import infra ORM model / method 內 new RepoImpl ✅ 無(走注入的 domain service)
raise ValueError/Exception(應用 ErrorCode) ✅ 無(用 GrcErrorCode
public method @transaction ✅ 齊全(2 個例外為 false positive:build_task_combinations 純運算不需、assert_project_participant 僅在 @transaction method 內被呼叫的 re-entrant guard)
權限檢查在 service 層 assert_project_participant(C16)

結論:BE 版完全合規,且比套件版乾淨(套件版在 method 內 WorkflowExecutionDomainService(WorkflowExecutionRepoImpl()) 直接實例化 repo;BE 版走 DI 注入的 domain service)。唯一小建議:assert_project_participant 命名無底線但實為內部 guard,可加底線或 docstring 標「caller 須在 @transaction scope」——cosmetic,不影響運作。

6. 未來反悔條件(trigger)

  • 出現第二個稽核/合規產品要共用 AP/生命週期/通知編排 → 啟動「抽 jedi-audit-workflow 套件層」(非合併進 flow-engine),屆時才做副作用 un-weave。
  • BE 版與套件版同源 bug 反覆各修各的造成 drift 痛點 → 重評。
  • 以上皆無 → 維持方案 c,本 arc 結案。

6.1 純通用能力下沉套件(refine 候選,與「合併」無關,不急)

承上架構討論:把 GRC 編排整進套件永遠是錯的(依賴倒置 + 污染 bounded context)。但有一類例外 —— BE 自己刻了、但本質上屬於通用 BPMN 引擎的能力,理論上該下沉到 jedi-flow-engine(讓引擎原生支援),而非留在 BE fork。候選:

候選能力 目前在 BE 下沉條件(動手前必先 verify
巢狀 sub-process 遞迴實體化 _instantiate_sub_workflow_create_leaf_jobs 確認遞迴邏輯不含 GRC 假設(純 BPMN call-activity 展開)才可下沉
job_execution_uid 注入 XML _patch_template_xml_job_uids_inject_job_execution_uid 確認是通用「實體化時把 execution uid 寫回模板 XML」機制、非 GRC 專屬

注意事項:

  • 這是「把純通用能力從 BE 搬套件」,跟「把 GRC 編排搬進套件」相反方向——前者對、後者錯。
  • 未驗證:以上兩項是否真的 GRC-free 尚未逐行確認;列為候選,動手前先讀 source 確認無 AP/Project/Survey 耦合。
  • 收益小(少 BE fork ~150 行)、非急;與方案 c 結案不衝突,純粹標記未來若動 flow-engine 時可順手評估。

7. 結案

本 arc 以方案 c 結案,不開實作。FR-034 狀態標「feasibility done — 方案 c」。