System Design · Diagram-first reference
讓 LLM 理解語言,
讓 Domain 守住事實
三張圖回答三個架構問題:依賴應朝哪裡、入帳應依什麼順序、交易歷史可以如何改變。模型只產生候選草稿;合法性、狀態與審計語意都由確定性的領域模型掌握。
本頁目錄
先記住三個判準#
不是把所有文字畫成框,而是用圖定位責任、時間與合法轉移。
聊天平台、LLM 與資料庫都可替換;核心不 import 外部 SDK。
ParsedExpenseDraft 是候選資料,不是已成立的 Expense。
不硬刪歷史;已撤銷不可再修改。
System Boundary / Ports & Adapters#
問題:哪些元件可以替換,而程式碼依賴必須朝哪裡?
閱讀重點:實線箭頭是依賴方向,不是訊息流。外部 adapter 依賴內部 port;Domain 不反向認識平台或儲存。
Telegram/LINE(概念性 interface) → Application Use Cases → Expense Domain Google Sheets/Postgres adapter → 實作 ExpenseRepository Port → Expense Domain 依賴規則:所有依賴向內;Domain 不認識聊天 SDK、LLM 或資料庫。
結論:替換聊天平台或儲存技術時,Domain 與其測試不需要改變。
Record Expense Sequence#
問題:一則自然語言輸入,何時才有資格成為永久交易?
閱讀重點:解析結果先停在 ParsedExpenseDraft;重複檢查與 invariants 都通過後,Repository 才收到 save。
User → Bot → LLM Parser → ParsedExpenseDraft → RecordExpense Use Case Use Case → Repository:檢查 idempotency;若已存在,回傳同一結果 Use Case → Domain:建立 Money/Expense 並檢查 invariants 全部通過後 → Repository.save(persist 永遠最後)
結論:LLM 不直接寫入;合法的 Domain 物件是 Repository 接受保存的唯一輸入。
Expense State Machine#
問題:交易成立後,如何修正或撤銷而不抹掉歷史?
閱讀重點:Active 是唯一可操作的既成交易狀態;Corrected 與 Voided 都是不可回頭的歷史結果。
ExpenseCorrected Domain Event;撤銷進入終態 Voided,禁止再修改,也不刪除原始紀錄。Draft ──confirm + invariants pass──→ Active Active ──correct──→ Corrected + ExpenseCorrected Domain Event Active ──void(reason)──→ Voided(終態;不可修改) 所有狀態:不硬刪,保留原值、變更與事件軌跡。
結論:「改資料列」不是領域行為;Correct 與 Void 必須是具名稱、規則與事件的明確 command。
把圖落成可測試的規則#
圖已定義責任與順序;實作只需守住以下少量契約,不再重複敘述每個節點。
格式驗證只證明資料形狀;Domain 仍逐一執行金額、幣別與狀態規則。
不同 adapter 通過同一套 contract tests;儲存格式不滲入 Aggregate。
所有改變由 domain behavior 觸發,並在需要時產生 Domain Event。
| 邊界 | 可以做 | 不可以做 |
|---|---|---|
| LLM Parser | 解析文字、標示缺漏與歧義 | 建立 Entity、判定合法、直接 persist |
| Application Use Case | 編排 port、transaction、idempotency 與回覆 | 複製 Domain invariant 或組供應商 payload |
| Expense Domain | 建立 Money、守 invariants、執行狀態轉移、產生事件 | import 聊天、LLM、SQL 或試算表 SDK |
| Repository Adapter | 實作保存、載入與原子性去重契約 | 繞過 Aggregate 直接改狀態或硬刪歷史 |
即使採用 graph orchestration,它也只能編排 use case;graph state 不能冒充 aggregate state,prompt 也不能成為規則權威。
Acceptance checklist#
用替換與失敗情境驗證架構,而不是只檢查 class 名稱。
- LLM adapter 可換成 deterministic test doubleRecordExpense 的合法性結果不變。
- Google Sheets 與 Postgres 可通過同一套 repository contract tests包含原子性 idempotency 行為。
- Domain tests 不需要網路、SDK 或資料庫同一 command 必然得到同一規則結果。
- 缺漏或歧義不會觸發保存應回覆澄清,而不是由模型補成事實。
- Correct 與 Void 都有明確 command 與審計結果Voided 不可再修改,歷史不硬刪。
- 三張圖在窄螢幕可安全水平滑動頁面本身不產生全頁水平 overflow,文字備援不依賴 JavaScript。
拔掉模型、聊天平台與特定資料庫後,Domain 規則仍能獨立執行;外部元件只改變如何理解、傳輸與保存,不改變什麼是合法交易。