iT邦幫忙

0

一張表單,能不能只描述一次?

  • 分享至 

  • xImage
  •  

一張表單,能不能只描述一次?

十分鐘影片導讀:從空專案、Excel 活頁簿到四種前端

polhem-video-guide

Polhem 示範影片

影片是英文旁白,YouTube 上可以切換英文或繁體中文字幕。

今年 iThome 鐵人賽我寫了 30 篇「ERP 架構師筆記:定義驅動的框架設計」,談定義驅動的機制與取捨,當時用的框架是 Bee.NET。連載結束後,Bee.NET 由 Polhem 接手,套件從 Bee.* 換成 Polhem.*。定義驅動的機制沿用下來,框架本身做了大幅重構,另外加上專案範本與 Excel 匯入。Bee.NET 已停止更新。

Polhem 是以 MIT 授權開源的 .NET 企業應用框架,原始碼在 GitHub,套件發佈在 NuGet。前端用 Avalonia,同一套 UI 程式碼可以跑在桌面、瀏覽器、iOS 與 Android。

鐵人賽那 30 篇幾乎都是文字,這次錄了一支影片把流程實際操作一遍:以 Northwind 為例,從空專案開始,匯入幾份 Excel 活頁簿,系統就能執行;需求改了,改定義,系統跟著變。過程中沒有寫 CRUD 程式碼,也沒有寫 SQL。下面照影片的順序,說明每一段用到的設計。


1️⃣ 開場:一個欄位為什麼要寫很多次

業務系統裡的一個欄位,本身只是幾項資訊:名稱、型別、是否必填。可是這幾項資訊會分別寫進 entity、migration、DTO、驗證、API、表單、清單和翻譯。加一個欄位,這些地方都要改,而且要改得一致。

工具再好,也只是讓每一處寫得快一點,要改的地方並沒有變少。所以我在意的是源頭放在哪裡:程式碼,還是定義?

Polhem 選擇定義。每張表單用資料描述一次,資料表、CRUD SQL、驗證規則和畫面都從這份描述產生。同一份描述可以跑在 SQL Server、PostgreSQL、MySQL、Oracle 與 SQLite 上,換資料庫只要改設定。前端在執行時讀取定義,所以改了定義不必重新編譯部署。

描述也不一定要寫成程式碼。影片用的是六份事先準備好的 Excel 活頁簿:類別、供應商、客戶、貨運商、產品與訂單,每一列是一個欄位。


2️⃣ 建立專案與準備資料庫

在 VS Code 新增 .NET 專案,從範本清單選「Polhem App (Avalonia)」,資料庫用預設的 SQL Server。產生的專案裡有伺服器、桌面前端和放定義檔的 Define 資料夾,Polhem 套件與 SQL Server 驅動都已經引用。

接著執行 VS Code 工作「Polhem: Initialize database」,它會建立 common、company、log 三個資料庫、框架用的資料表,以及一個管理者帳號。影片在執行前後各切到 SQL Server 看一次,前後對照很清楚。

分成三個資料庫,是為了讓框架自己的資料(登入狀態、快取通知)和各公司的業務資料分開,稽核記錄另外放。同一套程式服務多家公司時,各公司的業務資料互不相干,框架資料則共用。


3️⃣ 匯入 Excel:活頁簿就是規格

這段在影片裡最長。匯入前先打開訂單的活頁簿,看它寫了哪些東西:

  • 欄位名稱與資料型別,型別決定資料庫欄位的型態
  • 文字欄位的長度上限
  • 輸入控制項,例如狀態用下拉選單
  • 關聯的表單:客戶欄位指向 Customer 表單,匯入後會自動補上客戶代號與名稱,畫面上可以挑選客戶
  • 金額的公式:數量乘以單價,再扣掉折扣
  • 另一張工作表列出存檔前要檢查的規則

然後在 DefineEditor(Polhem 的定義檔編輯器)批次匯入六份活頁簿,每匯入一份就升級一次資料庫。匯入完,每張表單都有了自己的一組定義檔,業務資料表也建好了。

Excel 只是輸入的管道,匯入產生的定義檔跟手寫的沒有差別。各定義檔的分工如下:

定義檔 描述什麼 用在哪裡
FormSchema 表單的欄位、關聯、計算欄與規則 其他定義由它產生,CRUD SQL 也依它組出
TableSchema 實體資料表的結構 由 FormSchema 產生,用來建立與升級資料表
FormLayout 欄位怎麼排列 由 FormSchema 產生,各種前端依它畫出畫面
語系檔 欄位標題的各國語言 畫面上顯示的文字

FormSchema 描述的單位是表單,不是資料表。一張訂單有主檔和明細,客戶欄位關聯到另一張表單,這些都寫在表單這一層,SQL 由框架依此產生。


4️⃣ 執行:沒有表單程式碼的系統

按 F5,伺服器和桌面前端一起啟動。用剛才建立的管理者登入,選單上已經有主檔與交易兩組表單,排列方式就是 DefineEditor 裡那棵功能樹。

影片先輸入幾筆主檔,再開一張訂單:在明細挑產品,輸入數量和單價,金額自己算出來。故意不選客戶就存檔,會被擋下,擋下它的就是活頁簿規則表裡的那一條。選了客戶,代號和名稱一起帶入,再存一次就成功了。

金額計算和存檔檢查都有作用,但沒有為這張表單寫任何程式。資料在各層之間用 DataSet 傳遞,不必替每張表單建型別或 DTO;計算欄和規則寫在定義裡,由框架執行。


5️⃣ 疊代:需求改了,改定義

我最想讓人看的是這一段。系統上線後,需求一定會變:業務想替客戶分等級,訂單要限制數量。

這次直接在 DefineEditor 修改。客戶表單加一個「等級」欄位:文字、長度 10、預設 B,選項 A、B、C,用下拉選單。存檔後同步表單,TableSchema、版面和清單都加上這個欄位;只升級這張表單的資料表,客戶資料表就多了等級欄。訂單表單再加一條規則:明細數量不能超過 100。

回到前端登出再登入,不用重新啟動。客戶清單出現等級欄,既有客戶都是預設值 B;編輯畫面有下拉選單;訂單數量輸入 200 存檔,被新規則擋下。

背後是兩個機制。資料庫升級會比對定義和現有資料表,只補上缺少的部分,既有資料不動。定義存在快取裡,修改後快取失效,前端下次讀到的就是新的定義。


6️⃣ 視覺化細調與多端

影片最後還有兩小段。

一段是在 DefineEditor 調整細節:聯絡人改成必填,版面從兩欄改成三欄,替「等級」加上中文標題,預覽畫面立即更新。重新登入後,三欄版面和必填檢查都生效,用中文介面的使用者會看到新標題。

另一段回到建立專案時的資料夾。同一個專案除了桌面,還有網頁、iOS 與 Android 前端,執行一個工作就能把伺服器和四個前端一起啟動。四個前端的選單和訂單都一樣,因為讀的是同一份定義,連的是同一台伺服器。

FormLayout 只描述有哪些欄位、怎麼排,不綁定 UI 技術,每種前端各自依它畫出畫面。前端技術過幾年就會換一輪,定義可以留著繼續用。


7️⃣ 影片沒有演到的部分

示範挑的都是定義能完整表達的流程。為了不讓人誤會所有表單都能這樣做完,補充幾點:

  • 定義表達不了的邏輯,還是要寫程式。Excel 不描述程式碼。框架裡完整的 Northwind 範例,訂單綁了一個自訂業務物件,負責訂單總額與明細檢查,影片示範的是只用定義的版本。共通的部分交給定義,特殊的業務邏輯在業務物件的擴充點覆寫。
  • 報表與批次作業不走定義。CRUD 由定義驅動,報表和批次由業務物件自己實作。
  • 權限、稽核與傳輸加密框架都有,影片沒有示範。

✅ 結語

影片的流程用一句話說,就是描述表單,其餘交給框架。做得到,是因為定義是唯一的源頭,資料庫、SQL、畫面和規則都從它產生,改了定義,各層會一起更新。

這套做法比較適合表單多、形式規律、要長期維護和客製的系統,例如 ERP。如果系統表單不多,每個畫面都高度客製,直接寫程式可能更省事。不用 Polhem 也沒關係,把「讓定義當源頭」的想法帶走,用自己熟悉的工具實作也行。

想自己試的話,範本用 dotnet new install Polhem.Templates 安裝,文件與範例在 GitHub。


📘 HackMD 原文筆記:
👉 https://hackmd.io/@jeff377/polhem-video-guide

📢 歡迎轉載,請註明出處
📬 歡迎追蹤我的技術筆記與實戰經驗分享
Facebook | HackMD | GitHub | NuGet


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言