本文探討如何提升 Genero 系統的可組合性,以及如何將龐大的單體式應用程式拆分為更小、更易管理的組成部分。所謂「可組合性」,指的是你的系統與其他系統之間的整合能力。
背景
過去,Genero 系統通常以獨立的單體架構運作。然而現代需求要求這些系統能與其他應用程式介接、部署在雲端分散式環境,或允許以第三方解決方案替換其中部分功能。
核心定義
- 應用程式/程式(Application/Program):單一個含有 MAIN 的 Genero 程式。
- 系統(System):實作某項業務功能的所有 Genero 程式打包在一起。
- 模組(Module):系統中包含多個程式的子集合。
程式碼規範
文章強調,程式碼規範是建構可組合系統的核心基礎,其意義遠超過單純的撰寫慣例——它能確保系統長期持續成功運作與可靠性。
以明暗主題切換為例:要支援此功能,規範必須禁止硬編碼顏色值。正確做法是使用呈現樣式(Presentation Styles):
ERROR error_text ATTRIBUTES(STYLE="error")
而非:
ATTRIBUTES(RED)
規範的目的在於確保「解決方案的各個部分」能在定義好的介面上可靠地運作。
程式碼審查
自動化與人工審查都是維持規範遵循度的必要手段。自動化審查能即時發現違規;人工審查則能捕捉難以自動化偵測的問題。
Genero Studio 提供 Code Quality 工具以協助規範執行。審查時應特別留意模組介面的部分。
現代化程式碼方式
系統必須採用現代技術——例如 RESTful Web Services 與 JSON——才能與現代應用程式保持互通性。文章指出:「如果世界上其他系統都使用 RESTful Web Services、JSON……你也應該做好使用它們的準備。」
IMPORT FGL 與 PACKAGE
相較於傳統的 fgllink 方式,結構化的 import 機制帶來以下好處:
- 在檔案開頭清楚可見相依關係
- 編譯器可驗證函式參數
- 改善編輯器的自動補全功能
- 更易於管理建置規則
建議:「你應該評估從建置流程中移除 fgllink 使用所需的工作量。」
避免循環相依
循環相依會導致結構不清晰,應予以避免。循環關係意味著所有相依檔案必須一起使用,降低了模組化程度。例外處理程式碼應自給自足,不應引用函式庫。
全域變數
指引方向仍是消除 GLOBALS,改用 IMPORT FGL 與 PUBLIC 變數,依照移轉指南逐步完成。
相依關係圖
Genero Studio 的 Dependency Diagram(相依關係圖)可視化呈現原始碼檔案之間的關係。初期圖表可能相當複雜,但在模組結構正確建立後,透過篩選與摺疊應能逐漸簡化。
當程式碼組織良好時,相依箭頭應向上流動,基底在上方,端點在下方。
命名慣例
使用 2 至 3 個字母的短碼在檔名中標識模組或用途,方便快速識別有效的引用來源。例如,一個 ERP 系統可能使用:lib(函式庫)、sys(系統)、gl(總帳)、dl(應收帳款)、st(庫存)、so(銷售訂單)、hr(人力資源)。
按業務功能拆分
模組將相關程式集中管理。程式碼規範強制執行邊界——「gl」的資料庫資料表只能由「gl」的原始碼存取,「gl」的表單只能從「gl」的檔案開啟。
對於模組間的溝通,文章建議採用 Web Services 而非直接引用:
錯誤做法(直接存取資料表):
SELECT dl_current_balance FROM dl_balance
正確做法(透過 Web Service 介面):
so_dl_interface.get_current_balance()
這樣就能替換特定模組的實作,而不需要修改呼叫端的程式碼。
按程式碼功能拆分
進一步的分離包括:
ui:使用者介面(User Interface)br:業務規則(Business Rules)ct:控制器(Controller)ws:Web Servicedb:資料庫(Database)rp:報表(Report)
關鍵規則:「OPEN WINDOW、INPUT、ERROR、DISPLAY 等使用者介面語法,只能出現在檔名包含 _ui_ 的 4gl 檔案中。」
此分離能確保安全地對外公開 Web Service。業務邏輯不得包含 UI 程式碼。
有問題的程式碼範例:
#! ?_br_?.4gl
FUNCTION validate_number(number) RETURNS (ok)
IF number < 0 THEN
ERROR "Number must not be negative" -- 錯誤:br 檔案中含有 ui 程式碼
正確做法:
#! ?_br_?.4gl
FUNCTION validate_number(number) RETURNS (ok, error_text)
IF number < 0 THEN
RETURN FALSE, "Number must not be negative" -- 正確
如此一來,業務邏輯便可在 GUI 與 Web Service 情境下重複使用。
資料庫與報表的獨立性
將資料庫與報表程式碼分離到獨立檔案,能更輕鬆地因應資料庫切換(包含 NoSQL)或報表工具更換。
Web Services
文章鼓勵優先探索高階的 RESTful Web Services 框架,而非 SOAP 或低階做法。
建議的三個練習,展示循序漸進的過程:
- 將一個簡單函式公開為 Web Service,並用 curl 或 Postman 呼叫它。
- 在 Genero 中呼叫第三方 Web Service。
- 建立兩個程式,一個公開功能,另一個加以呼叫。
錯誤處理注意事項:呼叫 Web Service 需要比直接函式呼叫更完整的錯誤處理,程式碼必須考慮服務無法使用或發生失敗的情況。
效能影響:由於網路額外負擔以及參數的封裝與解封,Web Service 呼叫比直接呼叫慢。在迴圈中的效能損耗會被放大。
最佳化方式:將重複呼叫合併為單次 Web Service 請求。例如,與其逐一查詢每位客戶的餘額,不如傳入客戶代碼陣列、一次回傳餘額陣列,減少網路來回次數。
使用者介面的可組合性
當應用程式來自多家廠商時,一致的使用者體驗至關重要。Genero Browser Customization 能透過顏色、圖示及 Date Picker 等元件的自訂,讓介面風格與第三方應用程式保持一致。
總結
作者指出,部分開發者從 1990 年代起就已落實這些規範。許多組織已部分具備這些元素,但缺乏明確定義的介面。
建議:使用 Dependency Diagram 審視你的程式碼庫。組織良好的程式碼「一旦正確,看起來就是對的。」