
昨天結尾說今天把三十天的東西收成全圖。
本篇說明:
FormSchema 長出來的東西,與每一次呼叫都會經過的東西三段深度不互斥,同一張表單可以三段都用上:
| 深度 | 寫的是什麼 | 改它要付什麼 |
|---|---|---|
| NoCode | 定義:FormSchema、TableSchema、FormLayout,欄位上的 FormRule 與 ValueExpression |
換一份定義 |
| LowCode | 覆寫 BO 的步驟、業務外掛 | 編譯、打包、部署 |
| AnyCode | 自訂前端、BO、Repository、ExecFunc 的方法 |
編譯、打包、部署 |
改定義就能完成的是 NoCode;在框架切好的步驟與時點補一段程式的是 LowCode;框架的流程接不住、整段自己寫的是 AnyCode。
每一家做法都一樣的邏輯,會從 LowCode 逐步收進定義與框架,流程見 Day 29 的「從應用收斂到框架」。
| 路 | 回答的問題 | 表單變多時 |
|---|---|---|
| 縱的那條 | 這張表單長什麼樣 | 跟著線性成長 |
| 橫的那條 | 這一次呼叫發生了什麼 | 不變 |
縱的那條:
定義(唯一真相)
FormSchema 為中樞
│
┌───────────────────────┼───────────────────────┐
│ 產生 │ 產生 │ 求值
↓ ↓ ↓
版面 TableSchema FormRule
→ 單筆/清單/開窗 → 比對現況後升級 → 存檔與刪除的時點
增修刪查的語句 DataSet 的結構 ValueExpression
→ 執行期由定義現組 → 不必有實體類別 → 改動相關欄位的當下
橫的那條:
前端 → Connector → 序列化 → 壓縮 → 加密
↓
端點 → 存取控制 → session → BO(擴充點與外掛)
↓
Repository → 資料存取 → 資料庫
橫切在兩條路上的機制:
| 機制 | 篇 |
|---|---|
| 定義快取與跨節點失效 | Day 12 |
| 錯誤契約 | Day 18 |
| 客製疊加與多語系 | Day 19–20 |
| 權限的判定與套用 | Day 24 |
| 稽核 | Day 25 |
| 數值捨入、時間語意與時區換算 | Day 26–28 |
圖上各處的機制,反覆用到幾條相同的原則:
| 原則 | 例子 |
|---|---|
| 位置決定語意 | 擴充點在 transaction 的哪一邊(Day 14)、值放在 session 的哪一段(Day 17)、翻譯查不到時由哪一層頂上(Day 20) |
| 失敗要看得見 | 快取舊了看不看得出來(Day 12)、ProgId 綁錯型別時是當場報錯,還是看起來照跑(Day 15) |
| 呼叫端送上來的每一樣東西都是主張,不是事實 | 型別名要先過白名單(Day 22)、PayloadFormat 要對上方法宣告的等級(Day 23)、部門欄要回資料庫再查一次(Day 24) |
圖上的路是提供的,不是強制的:自己開連線、自己拼語句、繞過入口,橫切的機制都不會生效。
Day 2 進場前問了三題,這裡各補一句,再加兩題:
| 問題 | 進場前的答案 | 走完之後補充的 |
|---|---|---|
| 畫面是不是制式表單 | 版面收斂成主檔區塊加明細區塊,儀表板與排程不在模式裡 | 模式外的畫面走手動排版,接資料那一段與表單共用(Day 7) |
| 請求是不是一筆單據 | DataSet 自描述,每次回應帶著結構一起送 |
定義必須快取,變更靠通知傳到各節點(Day 12) |
| 系統要活多久 | 成本集中在前期,回報要好幾年才顯現 | 回報落在哪裡見下表 |
| 是不是要接既有的資料庫 | Day 2 沒問 | 既有資料表要先改成框架的系統欄位(Day 3) |
| 會不會出現「第二個」 | Day 2 沒問 | 第二家公司、第二種語言、第二個角色、第二種幣別;不會出現的話,客製層、多語系、權限模型與幣別主檔這類機制都是純成本(Day 29) |
Day 1 說過,短期看 Code-First 幾乎必勝。定義驅動的回報在長期:
| 回報 | 落在哪裡 |
|---|---|
| 開發流程 | 同一件事只有一套做法,改定義到生效之間沒有編譯、打包、部署 |
| 維護成本 | 上千份同構的 FormSchema 可以用一支腳本檢查,例如每一個 Currency 欄位是不是都標了 NumberKind |
| 教育成本 | 看懂一張表單就看得懂全部(Day 2);擴充點是列出來的,覆寫哪一步、掛在哪一個時點都有固定的名字 |
| 使用者訓練 | 版面詞彙少,訓練成本不跟表單數量成正比(Day 7) |
Day 29 分過邊界與待辦:刻意不走定義的是邊界,框架還沒做完的是待辦。下表依還沒完成的原因分類:
| 為什麼還沒完成 | 例子 |
|---|---|
| 該收斂而還沒收斂 | 單據編號規則,前綴加日期加流水號,現在每一家自己寫 |
| 宣告的形狀還沒接住 | 跨列聚合(至少一列明細、主檔總額);報表的查詢條件畫面,FilterNode 已經在了,缺版面產生器 |
| 機制在了,只做了一半 | 寫入前把值捨到欄位的容量,只有規範沒有實作(Day 26);transaction 之內還沒有擴充點;前端沒有登出流程;近端呼叫的服務容器還沒 DI 化;JSON-RPC 批次請求沒有實作 |
| 定義已經夠了,工具還沒做 | 由定義導出匯入格式與測試資料(Day 9) |
| 不是未竟,是不做 | 真正的業務判斷、報表與批次的 SQL、深度的畫面客製(Day 29);ValueExpression 不查資料庫 |
「宣告的形狀還沒接住」是進度,補上聚合函式與求值順序就能改成宣告。「不是未竟,是不做」是設計決定:例如 ValueExpression 如果能查資料庫,前後端就算不出同一個答案。兩者歸錯類的後果不同:把進度當成邊界,本來收斂得掉的東西會一直留在每一家的程式裡;把設計決定當成待辦,框架會長成什麼都能做,等於什麼決定都沒替你做(Day 2)。
「機制在了,只做了一半」這一類,讀文件時最容易誤以為已經做好:規範寫了卻沒有實作、一條路少了一段、做好了卻沒有呼叫端。以寫入前捨到容量為例,Day 29 那個沒給位數的 discount 欄哪天被填進小數,要不要捨入、怎麼捨入,就看當下那一家資料庫。
Day 1 說 ERP 的敵人是重述。定義驅動消滅的不是重述,是抄寫:一個欄位的事實仍然出現在 FormLayout、TableSchema、語句、DataSet、FormRule、稽核、權限與時區換算裡,差別是這些地方都讀同一份 FormSchema,不再各抄一份。
都讀同一份,那一份就得做到:
| 要求 | 篇 |
|---|---|
| 夠寬,承載得起結構以外的語意 | Day 2 |
| 有人保證每個節點拿到的都是最新版,否則會停在舊值 | Day 12 |
| 說得清楚誰改得動、改到哪一層為止 | Day 19 |
| 讓漏宣告看得見,因為漏掉之後剩下的值仍然合法 | Day 26 |
定義是結構化資料,Day 5 列過批次腳本、產生器與 AI 都接得上它。AI 寫得出程式碼之後,它的產出可以是一份定義:建置期診斷負責檢查,框架照著展開,同構的表單就能照同一套程序一件一件做出來。ERP 工程師的工作會因此移到框架、機制與這套程序上,要懂的是整個架構怎麼運作。
三十天到這裡結束。Day 1 說過,框架在這個系列只是用來證明這些想法不是紙上談兵;你完全可以不用它,只把「讓定義當源頭」這個想法帶走,用你熟悉的語言和工具去實現。
本系列同步發表於 HackMD,完整目錄