Architecture Case / DDD × AI · v2
Light · Wide · Diagram-first公開安全 Demo

System Design · Diagram-first reference

讓 LLM 理解語言,
讓 Domain 守住事實

三張圖回答三個架構問題:依賴應朝哪裡、入帳應依什麼順序、交易歷史可以如何改變。模型只產生候選草稿;合法性、狀態與審計語意都由確定性的領域模型掌握。

狀態:參考架構範圍:支出紀錄版本:diagram-first v2資料:完全虛構
本頁目錄
00

先記住三個判準#

不是把所有文字畫成框,而是用圖定位責任、時間與合法轉移。

Dependency所有依賴朝向 Domain

聊天平台、LLM 與資料庫都可替換;核心不 import 外部 SDK。

Order先解析與驗證,最後才保存

ParsedExpenseDraft 是候選資料,不是已成立的 Expense。

History修正與撤銷留下痕跡

不硬刪歷史;已撤銷不可再修改。

01

System Boundary / Ports & Adapters#

問題:哪些元件可以替換,而程式碼依賴必須朝哪裡?

閱讀重點:實線箭頭是依賴方向,不是訊息流。外部 adapter 依賴內部 port;Domain 不反向認識平台或儲存。

Diagram 1 · 可替換邊界與向內依賴Dependency → Domain
DDD AI 記帳系統的 System Boundary 與 Ports and AdaptersTelegram 與 LINE 作為概念性介面 adapter,依賴 Application Use Cases;Use Cases 依賴 Expense Domain。Google Sheets 與 Postgres repository adapters 實作 Repository Port,依賴朝向 Domain。 SYSTEM BOUNDARY · EXPENSE APPLICATION Telegram概念性 interface LINE概念性 interface Application Use CasesRecordExpenseCorrect / Void Expense Domaindeterministic business coreAggregate + MoneyInvariants + Events ExpenseRepository Portcontract defined inward Google Sheetsrepository adapterPostgresrepository adapter implements / depends inward所有程式碼依賴朝向核心 →
圖 1|入口只翻譯 transport;Application 編排 use case;Repository adapters 透過內層定義的 port 接入。平台名稱不代表任何真實整合。
無 JS 文字備援

Telegram/LINE(概念性 interface) → Application Use Cases → Expense Domain Google Sheets/Postgres adapter → 實作 ExpenseRepository Port → Expense Domain 依賴規則:所有依賴向內;Domain 不認識聊天 SDK、LLM 或資料庫。

結論:替換聊天平台或儲存技術時,Domain 與其測試不需要改變。

02

Record Expense Sequence#

問題:一則自然語言輸入,何時才有資格成為永久交易?

閱讀重點:解析結果先停在 ParsedExpenseDraft;重複檢查與 invariants 都通過後,Repository 才收到 save。

Diagram 2 · RecordExpense 的安全順序Persist is last
Record Expense sequence diagramUser 傳訊息給 Bot,Bot 呼叫 LLM Parser,Parser 回傳 ParsedExpenseDraft。Use Case 先向 Repository 查 idempotency,再呼叫 Domain 驗證 invariants,成功後最後才保存。 UserBotLLM ParserUse CaseDomainRepository 1 · UNDERSTAND LANGUAGE自然語言parse(input) ParsedExpenseDraft候選欄位 ≠ Entity 2 · PROTECT AGAINST DUPLICATESfindByIdempotencyKey(key)existing → return same result 3 · ESTABLISH BUSINESS FACTcreate(draft)check invariants 4 · PERSIST LASTsave(valid Expense + events)
圖 2|idempotency 在建立新事實前檢查;Domain invariants 在持久化前執行。若草稿含歧義或規則失敗,流程回覆澄清/錯誤而不 save。
無 JS 文字備援

User → Bot → LLM Parser → ParsedExpenseDraft → RecordExpense Use Case Use Case → Repository:檢查 idempotency;若已存在,回傳同一結果 Use Case → Domain:建立 Money/Expense 並檢查 invariants 全部通過後 → Repository.save(persist 永遠最後)

結論:LLM 不直接寫入;合法的 Domain 物件是 Repository 接受保存的唯一輸入。

03

Expense State Machine#

問題:交易成立後,如何修正或撤銷而不抹掉歷史?

閱讀重點:Active 是唯一可操作的既成交易狀態;Corrected 與 Voided 都是不可回頭的歷史結果。

Diagram 3 · 可審計的交易生命週期No hard delete
Expense state machineDraft 經確認後進入 Active。Active 可更正為 Corrected 並產生 ExpenseCorrected Domain Event,或撤銷為 Voided。Voided 不可修改,所有狀態都保留歷史而不硬刪。 Draft候選,可補資料confirminvariants pass Active已成立的交易 correct(command)Corrected保留原值與新值Domain EventExpenseCorrected void(reason)Voided終態 · 不可修改 歷史保存規則不硬刪 · 狀態轉移與 Domain Event 形成審計軌跡
圖 3|Draft 經規則確認後成為 Active;更正產生 ExpenseCorrected Domain Event;撤銷進入終態 Voided,禁止再修改,也不刪除原始紀錄。
無 JS 文字備援

Draft ──confirm + invariants pass──→ Active Active ──correct──→ Corrected + ExpenseCorrected Domain Event Active ──void(reason)──→ Voided(終態;不可修改) 所有狀態:不硬刪,保留原值、變更與事件軌跡。

結論:「改資料列」不是領域行為;Correct 與 Void 必須是具名稱、規則與事件的明確 command。

04

把圖落成可測試的規則#

圖已定義責任與順序;實作只需守住以下少量契約,不再重複敘述每個節點。

Parser contractStructured draft, never entity

格式驗證只證明資料形狀;Domain 仍逐一執行金額、幣別與狀態規則。

Repository contract去重必須具原子性

不同 adapter 通過同一套 contract tests;儲存格式不滲入 Aggregate。

Aggregate contract沒有 public setter

所有改變由 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 也不能成為規則權威。

05

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 規則仍能獨立執行;外部元件只改變如何理解、傳輸與保存,不改變什麼是合法交易。

公開案例聲明:本文為完全虛構的架構示例。未包含任何真實人物、帳號、公司、客戶、檔案路徑、識別碼、憑證、token、私有指標或生產環境細節。