
Day 11 處理 Debug:先找出哪一層真的出錯,不要因為 AI 能跨模組就把整條鏈一起改。
Day 12 再把需求寫成可判定 PASS / FAIL 的驗收條件(Acceptance Criteria,AC)。
但代點餐(delegated ordering)帶來的是另一種問題:有些需求本來就必須跨層成立。
一開始很容易把「代點餐」想成:
前端多選一個人,API 多帶一個
targetUserId,後端替他建立訂單。
實際走進系統後,問題變成:誰在操作、畫面正在看誰、這次寫入操作(mutation)作用在誰身上,三者不一定是同一個人。
repo 的狀態規則把它們分得很清楚:
一個 targetUserId 必須穿過 UI、API、Auth、Domain、Persistence、Response,再回到 UI,而且每一層都不能把這幾種身份混在一起。
跨層需求先定義每一層必須守住的契約(contract),再決定修改哪些檔案。
delegated ordering 同時存在兩個主要身份:
actor
Admin / ProxyAdmin
按下送出的人
target
被代點餐的成員
擁有訂單與餘額變化的人
旁邊還有一個容易混淆的 View As subject:
View As subject
畫面正在看的成員
只能讀,不能因此取得 mutation 語意
Worker 不相信前端傳來的角色或使用者狀態。它會先從登入身份確認實際操作的人(actor),再解析這次要修改的對象(target)。
如果只是用 View As 查看別人的資料,這個觀看狀態不能直接拿來修改對方資料;Worker 會拒絕這類 mutation,避免把「只能看」誤當成「可以代替對方操作」。
這讓跨層設計的焦點很具體:同一個語意從入口走到資料落地,不能在中途變形。
如果只叫 AI:
幫 delegated ordering 加上 target user。
它很容易搜尋 targetUserId,哪裡缺就補哪裡。

但檔案都補到,不代表功能完整。我會先畫一條 Change Map:

User Action
→ Frontend target
→ API intent
→ Auth / Domain:actor + target
→ Persistence:target ownership
→ Response:target context
這張圖先問一件事:
每經過一層,哪個事實不能被改寫?
在 delegated ordering 裡,就是:
actor 可以替別人操作,但 order ownership 與 balance ownership 仍屬於 target。
回頭核對實作後,同一條規則在不同層有不同責任:
| Layer | 這層負責的 contract | 不該承擔的責任 |
|---|---|---|
| Frontend | 區分一般點餐、View As、delegated target;把結果更新到正確的人與畫面 | 決定伺服器授權 |
| API / Transport | 傳遞「這次要修改誰(target)」與 mutation request | 相信 client 傳來的 role / balance |
| Auth | 確認實際登入並操作的人(actor),禁止 View As 被當成修改權限 | 決定畫面怎麼顯示 |
| Domain | 解析 actor / target / delegated 狀態與日期、cutoff 規則 | 把 UI state 當 authority |
| Persistence | 訂單、扣退款都屬於 target,並留下「誰替誰操作」的紀錄 | 因 actor 是 Admin 就改寫資料擁有者 |
| Response / UI update | 回傳 target 的結果,讓 UI 更新正確對象的狀態 | 讓前端猜誰的餘額變了 |
其中一個很小、但很能說明問題的案例,是 mutation 成功後的 newBalance。
前端不能一律更新「登入者餘額」:
delegatedOrderUser.balance;repo 的 regression test 直接模擬:
cached member balance = 0
↓
delegated response.newBalance = -100
↓
delegated banner 立即顯示 -100
↓
member balance cache 清除
↓
重新進入餘額管理後再抓一次
↓
列表也顯示伺服器回傳的 -100
如果資料庫扣對人、Response 也回對數字,但 Frontend 把 newBalance 寫回 actor,使用者看到的仍然是錯的。
單層都 PASS,不保證整條 User Journey PASS。
Change Map 畫完後,不代表經過的每一層都要改。
MUST CHANGE
這次需求改變了這層的 contract
MUST VERIFY
功能經過這裡,但既有 contract 應維持
OUT OF SCOPE
和本次 Acceptance Criteria 沒有直接關係
套回 actor / target contract:
| Area | 判定 | 原因 |
|---|---|---|
| delegated target UI state | MUST CHANGE | 使用者需要明確進入/退出代點情境 |
| API target intent | MUST CHANGE | request 必須指出 mutation target |
| authenticated actor resolution | MUST VERIFY | 登入身份的判斷規則已存在,不應為新功能重寫登入 |
| View As read-only protection | MUST VERIFY | 確認 delegated flow 沒有繞過它 |
| target-owned order / balance | MUST VERIFY / CHANGE only if broken | 訂單與餘額仍必須屬於 target,不因 actor 代操作就改寫 |
| unrelated identity onboarding | OUT OF SCOPE | 不屬於這條 User Journey |
這個區分對 AI 很重要。它擅長一次追很多 call chain,也容易把「相關」理解成「順便整理」。
Change Map 要控制的不是 AI 能看到多少,而是把「相關」再切成:
需要修改,還是只需要證明沒有被破壞?
前端可能先限制某個操作,Worker 也會再檢查一次。
兩者的責任不同:
Frontend
避免使用者走進明知不成立的流程
= UX guard
Worker
不論 client 怎麼送 request,都守住 actor / target / authority
= Security + business authority
真正危險的是 Frontend 複製完整 server policy,或 Worker 假設 UI 已經擋過某個情況。那時同一條規則才開始散落。
所以 Change Map 也不是消滅所有重複檢查,而是確認每個檢查服務哪個邊界。
跨層 Feature 不能只分別問:
delegated ordering 的完整 contract 更接近:
Admin / ProxyAdmin 發出 intent
↓
server 先確認實際操作的人與權限
↓
再確認 target 是否可被代操作
↓
order / 扣款 / 退款都屬於 target
↓
actor 自己的餘額不被誤改
↓
response 回傳 target 的 newBalance
↓
UI 更新 target 的畫面狀態
AI 很適合搜尋整條 call chain,但「找得到所有地方」和「知道哪些地方該改」是兩回事。
有了 Change Map,AI 可以沿著整條流程去找 contract 的實作點、應該檢查結果的位置,以及 MUST CHANGE / MUST VERIFY 的邊界,而不是把所有相關檔案都當成要修改。
這篇不需要把便當系統重新包裝成 Clean Architecture,也不要求每個需求硬拆成五層、七層。
可以帶走的流程很短:
Acceptance Criteria
↓
畫出 User Journey
↓
標出每層必須守住的 semantic contract
↓
標 MUST CHANGE / MUST VERIFY / OUT OF SCOPE
↓
最後才決定修改哪些檔案
delegated ordering 最容易出問題的地方,不是漏改一個 Component,而是功能一路能跑,某一層卻把「誰在操作」誤當成「資料應該屬於誰」。
下一步要處理的是:這些已確認不能壞的 contract,要怎麼在之後的修改中持續被驗證。
這是一套持續開發中的便當訂購系統。文章著重「為什麼」,GitHub 保留實際程式碼、測試與演進紀錄。
這篇主要核對:
PROJECT_STATE.md:actor/View As subject/mutation target 的身份邊界;worker-poc/src/domain/orderAuthorization.js:server-side actor / target resolution 與 View As mutation protection;tests/delegated-order-ui.test.cjs:target newBalance 與 member-balance cache regression。GitHub:https://github.com/henryfir456/bento-order-app