iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
Vibe Coding

從 GAS 到 Cloudflare D1:AI Agent 如何打造、接手並重構真實便當系統系列 第 13 篇

Day 13|一個需求跨五層時,我怎麼知道每一層各自負責什麼?

  • 分享至 

  • xImage
  •  

Bento Day 13

Day 11 處理 Debug:先找出哪一層真的出錯,不要因為 AI 能跨模組就把整條鏈一起改。

Day 12 再把需求寫成可判定 PASS / FAIL 的驗收條件(Acceptance Criteria,AC)。

但代點餐(delegated ordering)帶來的是另一種問題:有些需求本來就必須跨層成立。

一開始很容易把「代點餐」想成:

前端多選一個人,API 多帶一個 targetUserId,後端替他建立訂單。

實際走進系統後,問題變成:誰在操作、畫面正在看誰、這次寫入操作(mutation)作用在誰身上,三者不一定是同一個人。

repo 的狀態規則把它們分得很清楚:

  • 實際操作的人(authenticated actor):登入並發出操作的人;
  • 唯讀觀看對象(View As subject):畫面正在查看的人;
  • 寫入目標(mutation target):這次代點真正被修改的人;
  • View As 不會因此取得 mutation 權限。

一個 targetUserId 必須穿過 UI、API、Auth、Domain、Persistence、Response,再回到 UI,而且每一層都不能把這幾種身份混在一起。

跨層需求先定義每一層必須守住的契約(contract),再決定修改哪些檔案。

難題不是跨幾層,而是 identity 不能走丟

delegated ordering 同時存在兩個主要身份:

actor
Admin / ProxyAdmin
按下送出的人

target
被代點餐的成員
擁有訂單與餘額變化的人

旁邊還有一個容易混淆的 View As subject:

View As subject
畫面正在看的成員
只能讀,不能因此取得 mutation 語意

Worker 不相信前端傳來的角色或使用者狀態。它會先從登入身份確認實際操作的人(actor),再解析這次要修改的對象(target)。

如果只是用 View As 查看別人的資料,這個觀看狀態不能直接拿來修改對方資料;Worker 會拒絕這類 mutation,避免把「只能看」誤當成「可以代替對方操作」。

這讓跨層設計的焦點很具體:同一個語意從入口走到資料落地,不能在中途變形。

我開始用 Change Map,不再只列「會改到哪些檔案」

如果只叫 AI:

幫 delegated ordering 加上 target user。

它很容易搜尋 targetUserId,哪裡缺就補哪裡。

Day 13|真實 delegated ordering 操作起點:代點對象與訂單情境

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

Bento Day 13 actor-target cross-layer contract

User Action
→ Frontend target
→ API intent
→ Auth / Domain:actor + target
→ Persistence:target ownership
→ Response:target context

這張圖先問一件事:

每經過一層,哪個事實不能被改寫?

在 delegated ordering 裡,就是:

actor 可以替別人操作,但 order ownership 與 balance ownership 仍屬於 target。

Repo 裡每一層守的是不同 contract

回頭核對實作後,同一條規則在不同層有不同責任:

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。

前端不能一律更新「登入者餘額」:

  • self order:更新 authenticated user;
  • delegated order:更新 delegatedOrderUser.balance;
  • member balance cache 同時失效,下一次進餘額管理時重新向伺服器抓最新資料。

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。

MUST CHANGE 和 MUST VERIFY 要分開

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 能看到多少,而是把「相關」再切成:

需要修改,還是只需要證明沒有被破壞?

前端 guard 和 Worker authority 可以看起來重複

前端可能先限制某個操作,Worker 也會再檢查一次。

兩者的責任不同:

Frontend
避免使用者走進明知不成立的流程
= UX guard

Worker
不論 client 怎麼送 request,都守住 actor / target / authority
= Security + business authority

真正危險的是 Frontend 複製完整 server policy,或 Worker 假設 UI 已經擋過某個情況。那時同一條規則才開始散落。

所以 Change Map 也不是消滅所有重複檢查,而是確認每個檢查服務哪個邊界。

End-to-End Contract 看的是語意是否一路一致

跨層 Feature 不能只分別問:

  • UI 有沒有出現按鈕?
  • API 有沒有 200?
  • DB 有沒有新增訂單?
  • 餘額有沒有變?

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 的邊界,而不是把所有相關檔案都當成要修改。

Day 13 留下的是一張責任圖

這篇不需要把便當系統重新包裝成 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


上一篇
Day 12|沒有驗收標準,AI 只會很努力地做錯
下一篇
Day 14|測試不是最後補上去的,而是系統的「業務記憶」
系列文
從 GAS 到 Cloudflare D1:AI Agent 如何打造、接手並重構真實便當系統 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言