iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
Software Development

ERP 架構師筆記:定義驅動的框架設計系列 第 4

Day 4:定義檔的用途與分類

  • 分享至 

  • xImage
  •  

Day 4:定義檔的用途與分類

「定義是唯一真相」聽起來像是有一份檔案裝著全部真相。實際打開一個專案的定義目錄,看到的是十三種定義類型:有的是根目錄下的單一檔案,有的一支程式一份、散在子資料夾裡。

這一篇是地圖。後面每談一個機制都會用到其中幾份,先在這裡認識,就不必每次停下來解釋。第四節末尾有一張十三份的速查表,標出每一份由哪一天展開,讀到不確定的名字可以翻回這裡。

本篇說明:

  1. 啟動三件組:SystemSettings / DatabaseSettings / DbCategorySettings
  2. 一張表單的三份:FormSchema / TableSchema / FormLayout
  3. 綁定與導覽:ProgramSettings / MenuSettings / PluginSettings
  4. 所有表單共用的四份:LanguageResource / PermissionModels / CurrencySettings / UnitSettings
  5. 十三份共通的四件事:儲存體、快取、不可變更、找不到時的處理

表格第一欄可點進原始碼。


一、啟動三件組:這個部署跑在什麼上面

定義 存法 管什麼
SystemSettings 單檔 主金鑰從哪裡取、payload 怎麼打包與加密、偵錯模式
DatabaseSettings 單檔 這個部署有哪些實體資料庫、各自怎麼連上去
DbCategorySettings 單檔 哪一張資料表屬於哪一類、該類由哪一個資料庫承載

這三份是系統起得來的前提,而且讀取順序固定:

SystemSettings(指名主金鑰在哪裡)
  → 主金鑰解出保護設定用的金鑰
    → 解開 DatabaseSettings 裡的連線密碼
      → DbCategorySettings 寫的資料庫代號才對得到東西

順序不是慣例,是相依性算出來的唯一解。倒過來走的路根本不存在:在資料庫連得上之前,沒有任何機會先去資料庫查一把金鑰。

鏈條的第一份必須在什麼都還沒起來時就讀得懂,所以它自己不能加密。主金鑰因此不放在 SystemSettings 裡面,那份檔案只記「金鑰在哪裡」:

<MasterKeySource>
  <Type>Environment</Type>
  <Value>BEE_MASTER_KEY</Value>
</MasterKeySource>

框架也據此把啟動時那一次讀取獨立出來:不透過任何服務、不經過快取,直接讀檔,因為在那個時間點其他東西都還不存在。

資料庫的事為什麼是兩份

兩者回答不同的問題:

DatabaseSettings DbCategorySettings
回答什麼 這個部署有哪些資料庫、怎麼連 哪一類資料落在哪一個資料庫
性質 部署環境的事實 應用結構的事實
什麼時候改 搬機房、換資料庫伺服器、測試環境改指向 新增一張表、把某批資料換一個承載的資料庫
敏感度 裝連線密碼,要加密、要管存取 沒有秘密,開發者天天改

分家的好處是兩邊各改各的,順帶把敏感的與日常的隔開。

資料實際上會落在幾個資料庫、跟資料表命名前綴是什麼關係,Day 11 會專門談。

定義層裡有資料庫設定,卻不認得資料庫

一份記著連線字串的檔案歸在定義層,看起來像分層破了。沒有破,因為它放的是描述,不是連線。

定義層這個套件的引用清單極短,它不知道有資料庫、不知道有 HTTP、不知道有 UI,連線字串在它眼裡只是一段字串加一個代號。真正開連線的是資料存取層:定義層只宣告一個「拿得到 DatabaseSettings」的介面,由它去取用。


二、一張表單的三份定義

定義 存法 管什麼
FormSchema 一支程式一份 定義中樞:欄位、型別、跨表關連、主檔與明細、計算欄與驗證規則
TableSchema 一張表一份 實體資料表的欄位、型別、長度、允不允許 NULL、索引
FormLayout 一張版面一份 這些欄位在畫面上怎麼排

Day 1 那個「訂單加預計到貨日」要改的三處,就是這三份。

FormSchema 是中樞,另外兩份各自對著一個方向:一個對資料庫,一個對畫面。

  • FormSchema:宣告有哪些欄位、什麼型別、哪些指向別的表單、主檔配了哪幾張明細、哪些值是算出來的、存檔前檢查什麼。框架拿它組 CRUD SQL、決定資料跨層傳遞的形狀。
  • TableSchema:關心的是實體儲存,不是業務語意,所以長度、索引、精度可以由熟悉資料庫的人單獨調整。
  • FormLayout:三份裡唯一只管畫面的。怎麼排是設計階段的決定,寫成一份版面定義檔,執行期照它排。

三份的關係也決定改動順序:FormSchema 描述表單有哪些欄位,改動從它開始;TableSchema 跟著補實體欄位,要顯示才進 FormLayout


三、綁定與導覽:這支程式由誰接手、出現在哪裡

定義 存法 管什麼
ProgramSettings 單檔 ProgId 對到哪個 Business Object 與哪個 Repository,留空就走框架預設
MenuSettings 單檔 導覽樹的分組、排序、標題與可見性,每個項目指向一支程式
PluginSettings 單檔 每支程式在存檔與刪除的時點要跑哪幾個 plugin,依宣告順序執行
  • ProgramSettings 是一張註冊表,每支程式一列,決定這支程式的請求由誰處理。兩個綁定欄位留空就走框架預設的 CRUD 流程;填了類別名稱,就由那個類別接手。Day 3 說案例八張表單只有訂單要寫程式,看的就是這張表。
  • MenuSettings 跟註冊表分家,是因為讀它的不是同一端:註冊表由伺服端讀,裝的是程式碼裡的類別名稱;選單由前端讀,裝的是排序、標題與可見性。而且同一支程式本來就可能出現在選單的多個位置,所以節點有自己的識別碼,不用 ProgId 當鍵。
  • PluginSettings 記的是每支程式在存檔與刪除前後要跑的那幾個 plugin 與順序。跟繼承改寫的差別 Day 3 提過(繼承是取代、只能一個;plugin 是疊加、可以多個),細節留給 Day 21。

四、每一張表單都會用到的那幾份

定義 存法 管什麼
LanguageResource 每種語言一組,再依表單或分類切檔 在地化的標題與選項文字
PermissionModels 單檔 有哪些權限模型、各自管哪些動作、資料範圍怎麼界定
CurrencySettings 單檔 各幣別的小數位與最小流通面額
UnitSettings 單檔 各計量單位的顯示小數位

這四份不屬於任何一張表單,但每一張表單都會查到它們。

  • LanguageResource 是逐條疊上去的。FormSchema 裡本來就寫了欄位標題,語系資源提供各語言版本,對得上就換掉,對不上就用原本那個。不需要多語的專案完全不必碰,加了也不會逼你把每一條都翻完。
  • PermissionModels 登錄的是「有哪些權限可以談」。哪些動作需要授權、資料範圍怎麼界定,這些是模型;至於誰被授予了什麼,那是資料不是定義,不在這份檔案裡(Day 24 展開)。
  • 幣別與計量單位是系統層的:某個幣別有幾位小數、某個單位顯示幾位,全系統一致。而「這家公司准用哪幾種幣別、現金要捨入到多少」是公司層設定,跟著公司資料從資料庫來,不是定義檔。兩層各有位置,Day 26 會用到。

十三份速查

前面四節是依用途分組,這裡把十三份攤在一起,順序不變:

定義 管什麼 哪一天展開
SystemSettings 主金鑰來源、payload 選項、偵錯模式 本篇
DatabaseSettings 這個部署有哪些資料庫、各自怎麼連 本篇
DbCategorySettings 哪一類資料落在哪一個資料庫 本篇
FormSchema 定義中樞:欄位、型別、關連、主檔與明細、規則與計算欄 Day 5
TableSchema 實體資料表:欄位、長度、允不允許 NULL、索引 Day 6
FormLayout 這張表單在畫面上怎麼排 Day 7
ProgramSettings ProgId 綁哪一個 Business Object 與 Repository,只給伺服端 Day 15
MenuSettings 導覽選單的分組、排序、標題與可見性 Day 15
PluginSettings 每個 ProgId 掛哪些 plugin、依宣告順序執行 Day 21
LanguageResource 每種語言一組的標題與選項文字 Day 20
PermissionModels 有哪些權限可以談:動作與資料範圍策略 Day 24
CurrencySettings 各幣別的小數位與最小流通面額 Day 26
UnitSettings 各計量單位的顯示小數位 Day 26

這些名字在後面二十幾天會反覆出現,而且一律用原文。中文說法講不清楚是哪一份:
「分類檔」可能是 DbCategorySettings,也可能是 PermissionModels
「設定檔」十三份裡有八份都叫這個名字。


五、十三份共通的四件事

都是 XML,存在哪裡是另一回事

定義內容是 XML,放在哪裡可以換:磁碟上的檔案,或資料庫裡的一張表(一列一份定義,欄位裡放那段 XML)。框架透過同一個介面去讀,換一種放法,上面每一層都不必知道。

執行的時候用到的不是那段 XML。框架把它讀進來、還原成一個定義物件,之後要查什麼都是問這個物件。XML 只是它躺在磁碟或資料庫時的樣子。

讀進來之後就留在記憶體裡

快取裡放的就是這些還原出來的物件,一份定義一格,淘汰看的是使用頻率:被讀到就重新計時,一段時間沒人碰才丟掉。

  • 天天在用的那幾張表單,定義幾乎一直在記憶體裡
  • 一年開兩次的那張,用完沒多久就退掉
  • 伺服器不必為了沒人開的表單一直扛著它們的定義

這是定義切成一份一份的回報之一:快取的單位就是讀取的單位,記憶體用量跟得上實際使用量,而不是跟著定義總量走。

留著就有一件事要交代:源頭被改掉了怎麼辦。每一份快取各自盯著自己的來源,檔案就盯修改時間,資料庫就走另一套通知機制;來源一變,下一次要用的時候重讀一份新的(Day 12 展開)。

拿出來的定義不可以改

從框架取得一份定義,拿到的是共用的那一份,不是複本。同一台伺服器上,所有使用者拿到的是同一個物件。

代價是那份物件從此不能碰:

  • 要一份只給這個使用者看的視圖(例如把標題換成他的語言)→ 先複製再改
  • 要把變更寫回去 → 走框架的存檔方法,它寫檔並讓快取失效

被禁掉的只有「直接在共用那一份上動手」這一種寫法,而它本來就是錯的,改到的是所有人的東西。

找不到的時候,分兩類

框架內嵌了一組預設定義,但它在執行期完全不參與。框架只讀你指定的定義目錄,內嵌那組是開新專案時的起點,用工具複製進去,一次性動作。

不做回退是刻意的。如果 FormSchema 找不到就自動改用內嵌那份,系統會照樣跑起來,然後用著一份你根本沒放進去的定義。「你忘了部署那個檔案」會被改寫成「系統行為跟你想的不一樣」,而後者要花的追查時間高一個量級。

但「找不到」不是只有一種意思,所以十三種逐一分成兩類:

類別 行為 哪幾份
缺了就是設定錯了 直接報錯,訊息指名哪一個檔案 FormSchemaTableSchemaProgramSettingsPermissionModels,以及啟動三件組
缺了是合法答案 回一個「沒有」,由呼叫端接手 FormLayoutLanguageResource(用結構裡的標題)、CurrencySettings / UnitSettings(用預設位數)、MenuSettingsPluginSettings

這個分類寫在程式裡,而且必須由框架決定,不能讓每個應用各自解讀。否則同一個疏漏在不同模組會有不同表現,一個報錯、一個安靜地用了預設值,那比兩個都報錯難查得多。


六、案例:複製過來,再往上加

Northwind 用到框架提供的部門與員工兩張表單,做法就是上一節那個一次性動作:把預設定義複製進應用自己的定義目錄。

複製之後,應用自己在員工上加了兩個欄位,這兩行就在案例的 Employee.FormSchema.xml 裡:

<FormField FieldName="title" Caption="Title" DbType="String" />
<FormField FieldName="hire_date" Caption="Hire Date" DbType="Date" />

加完之後那份定義就完全歸應用所有,想怎麼改就怎麼改。反過來,框架日後在預設定義上新增的欄位不會自己跑進來,也不會有任何提醒;要不要跟上,自己去改那份檔案。

框架的那幾個系統欄位則不能拿掉,權限與組織的機制認的就是它們。

這是「定義是資料」在維護面上的具體樣子:一份資料複製出去自己維護,跟一支程式分支出去自己維護,要付的東西不一樣。前者不會有合併衝突,也不必逐版把主幹的修正搬回來,代價是新東西不會自動跟上。


十三份,一個真相

十三份的形狀大致是:三份決定系統跑在什麼上面,三份描述一張表單,三份決定誰來執行與從哪裡進來,四份是所有表單都會查的共用資料。

這十三份不是十三個設定檔,是同一個真相的十三個面向。它們彼此指來指去(DbCategorySettings 指向 DatabaseSettingsFormSchema 指向別的 FormSchemaProgramSettingsMenuSettings 各自指向同一支程式),卻不打架,靠的是每一份都只回答自己該回答的那個問題。

明天談 FormSchema 怎麼變成 SQL。


本系列同步發表於 HackMD,完整目錄


上一篇
Day 3:Northwind 案例的八張表單與自訂業務物件
下一篇
Day 5:FormSchema 如何驅動 SQL
系列文
ERP 架構師筆記:定義驅動的框架設計5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言