Project

General

Profile

CR #230 » CR-029_事件流W-2批次C消費端盤點與讀模型設計建議.md

內容更新:內部稱謂去識別化(2026-10-18) - Redmine Admin, 10/18/2026 05:32 AM

 

CR-029 事件流(W-2)批次 C:消費端盤點與讀模型設計建議

項目 內容
文件編號 CR-029
版本 1.2
提出日 2026-10-01
最後修訂 2026-10-02(1.2:C-5 全數結案——新增 §7.4 逐條結案表 + 「幽靈待辦」根因揭露(詳 CR-033)。1.1:§5 C-5 首項由 CR-031 §2 落地、C-7 由 CR-031 §3 裁定暫緩)
提出者 WorkBuddy AI(開發團隊)
緣起 BEAM PMO 指示「繼續批次 C(消費端),一樣擬定計劃、規格先行、任務追蹤清單缺一不可」。CR-027 §6 早已載明批次 C「需先立讀模型規格」(CSP-001 §5 條件 9:無 SCS 不寫程式)——本 CR 即該「先立規格」之前置盤點。
範圍 SAD-004 §5.2(權威事件清單)+ SAD-002 §3(各服務「事件(訂閱)」)所列、尚未實作之跨服務消費邊
⛔ 明確排除 SAD-003 §4.1 之 7 個 q_* 視圖——全部是「自投影」(見 §3),不是批次 C 之內容;屬 CR-027 §5 #6
影響範圍 SAD-002 §3、SAD-004 §5.2、SDD-006 §4.2、各服務 SCS-00x;GOV-006 §4.8
狀態 盤點完成;C-1/C-2 已完成;C-5 已全數結案(CR-033——實作 1/不做 7/待實作 0);C-7 已裁定暫緩(CR-031 §3);其餘批次依 CR-030 §4 之裁定處理

1. 結論摘要(先講重點)

  1. 批次 C 之真正障礙不是技術,是「目標讀模型/行為尚未被規格定義」——
    實測 6 個服務之 schema,除既有 5 條已完成者外,沒有任何一條消費邊有現成的目標表(見 §4)。
  2. 僅 2 條「語意來源明確 + payload 足夠 + 無冗餘同步路徑」→ 可立即動工(C-1/C-2)。
    ★ 其語意來源是既有種子模板(PAYMENT_RECEIVED/STATEMENT_ISSUED),不是我發明的。
  3. 兩條都撞上同一個契約缺口:模板需要 currency,而兩個事件的 payload 都沒有——
    此即 CR-027 §5 #10 之同族問題(#10 為 payment.posted.v1、#14 為 limit.hold.created.v1)。
    → 本 CR 依 SAD-004 §5.3(新增可選欄位不升版)補為可選,並登記待追認(比照 #14 之先例)。
  4. 另有 8 條屬「與既有同步呼叫重複」(§6)——若事件與同步並存,將重複套用(重複累點/重複佔用)。
    此為架構性問題,必須先裁定「事件是唯一路徑,抑或同步為主、事件僅供投影/通知」。
  5. 批次 C 不可「照單全做」:盲目實作會產出語意錯誤或重複套用的消費者——比不實作更危險。

2. 現況:已完成之消費邊(5 條,不在本批範圍)

# 事件 消費者 效果 依據
1 card.card.status-changed.v1 authorization card_status_caches 投影 #A-117/#A-118
2 limit.limit.adjusted.v1 authorization account_limit_caches(last_sequence_no) #A-119
3 limit.hold.created.v1/.released.v1 authorization limit_holds 投影(以 authorizationId 去重) #A-128
4 payment.payment.posted.v1 limit 恢復 account_limits.used_amount #A-124
5 authorization.authorization.approved.v1 notification 建立 AUTH_APPROVED 通知 #A-125

5 條皆有共同形狀:consumed_events(冪等)+ 目標投影表/服務呼叫 + 亂序防護(SAD-004 §5.4)。
本 CR 之 C-1/C-2 即複用此形狀(見 §7)。


3. 為何 SAD-003 §4.1 之 q_* 不在批次 C 之範圍

SAD-003 §4.1 列 7 個查詢視圖。逐列比對「來源事件」與「存放服務」:

視圖 服務(存放地) 來源事件 來源服務 是否同一服務
q_account_balance posting posting.balance.changed.v1 posting ✅ 自投影
q_available_limit limit limit.limit.adjusted.v1、limit.hold.* limit ✅ 自投影
q_transaction_history posting posting.transaction.posted.v1 posting ✅ 自投影
q_authorization_history authorization authorization.* authorization ✅ 自投影
q_points_balance rewards rewards.points.* rewards ✅ 自投影
q_statement_summary statement statement.statement.issued.v1 statement ✅ 自投影
q_merchant_settlement merchant merchant.settlement.submitted.v1 merchant ✅ 自投影

7/7 全部是自投影(寫模型 → 事件 → 讀模型,同一服務內)。

這正是 CR-027 §5 #6 所質疑者:自投影與該服務之權威表同名同義
(q_transaction_history vs posting.transactions、q_available_limit vs limit.account_limits…),
實作價值低且易與權威表混淆。
→ 本 CR 不處理 q_*;其去留依 #6 裁定(建議:改寫為跨服務投影,或廢除並以「各服務 CQRS 讀模型清單」取代)。
⚠️ 若 #6 裁定為「自投影要做」,則批次 C 之範圍須重新界定——請 BEAM PMO 留意此連動。


4. ★ 待實作之消費邊逐條判定(22 條,SAD-004 §5.2)

判定欄位說明:

  • 語意來源:該「消費後做什麼」有無文件或既有資料之依據(— = 未載明)
  • 目標模型:消費者 schema 中是否已有承接之表(實測 user_tables)
  • payload:事件 payload 是否足以完成該行為
  • 同步冗餘:該行為是否已由既有同步呼叫完成(並存即重複套用)
  • 判定:可做/需裁定/待生產端
# 事件 消費者 用途(SAD-004 §5.2) 語意來源 目標模型 payload 同步冗餘 判定
1 authorization.authorization.approved.v1 posting 佔用額度、產生待請款 — ❌ 無「待請款」表 ✅ ⚠️ auth 已同步呼叫 limit 佔用 需裁定
2 authorization.authorization.approved.v1 limit 佔用額度 — ✅ account_limits ✅ ⚠️ auth 已同步呼叫 limit 佔用 → 重複 需裁定(§6)
3 card.card.status-changed.v1 party 卡片狀態同步 SAD-002 §3.4 載訂閱 ❌ CC_PARTY 無卡片相關表 ✅ — 需先立讀模型規格
4 product.parameter.changed.v1 全體(16) 參數熱更新 SAD-002 §3.5「AP-04 參數驅動」 ❌ 各服務無參數快取表 ⚠️ 無 value(僅知「哪個參數變了」,須回查 product) ⚠️ 各服務目前同步呼叫 product 取參數 需裁定(範圍大)
5 posting.transaction.posted.v1 statement 帳務事件 — ❌ 無 ✅ ⚠️ statement 結帳時已同步查 posting 需裁定(§6)
6 posting.transaction.posted.v1 rewards 消費累點 RWD-5.1(累積規則已定義) ❌ 無「待累點」表 ❌ 缺 mcc/merchantId → 累點規則無法判定 — 需裁定(payload)
7 posting.transaction.posted.v1 clearing 帳務事件 — ❌ 無 ✅ — 需裁定(語意)
8 posting.transaction.posted.v1 limit 釋放佔用 SAD-002 §3.2 載「釋放佔用」 ✅ limit_holds ❌ 缺 authorizationId/holdId → 無法對映佔用 — 需裁定(payload)
9 posting.transaction.reversed.v1 statement 沖正事件 — ❌ 無 ✅ 同 #5 需裁定
10 posting.transaction.reversed.v1 rewards 沖回點數 RWD-5.1 退款沖回 ❌ 無 ⚠️ 有 reversalOf,惟需本地保存「消費→分錄」對照 — 需裁定(payload/模型)
11 payment.payment.posted.v1 posting 繳款入帳 — ✅ transactions ✅ ⚠️ payment 已同步呼叫 posting 入帳 → 重複 需裁定(§6)
12 payment.payment.posted.v1 notification 繳款通知 ★ 既有種子模板 PAYMENT_RECEIVED(EMAIL/SMS) ✅ notifications ⚠️ 缺 currency → 本 CR 補為可選 — ✅ 可做(C-1)
13 statement.statement.issued.v1 payment 帳單出帳 — ⚠️ 有 AUTO_DEBIT_ENROLLMENTS,惟「帳單成立後做什麼」未載明 ✅ — 需裁定(語意)
14 statement.statement.issued.v1 notification 帳單通知 ★ 既有種子模板 STATEMENT_ISSUED(EMAIL) ✅ notifications ⚠️ 缺 currency → 本 CR 補為可選 — ✅ 可做(C-2)
15 installment.plan.created.v1 limit 分期佔用 — ✅ account_limits ✅ — 待生產端(事件未實作)
16 installment.plan.created.v1 posting 分期佔用 — ✅ transactions ✅ — 待生產端
17 rewards.points.accrued.v1 notification 點數入帳 ❌ 無對應模板(種子僅 3 種) ✅ notifications ✅ — 需裁定(模板)
18 rewards.points.redeemed.v1 posting 點數兌換 — ✅ transactions ⚠️ 有 pointsUsed/amountPaid,惟「產生何種分錄」未載明 — 需裁定(語意)
19 rewards.points.redeemed.v1 notification 點數兌換 ❌ 無對應模板 ✅ notifications ✅ — 需裁定(模板)
20 clearing.batch.matched.v1 posting 清算比對完成 — ❌ 無 ✅ — 需裁定(語意)
21 dispute.case.opened.v1 posting 爭議受理 — ❌ 無 ✅ — 需裁定(語意)
22 dispute.case.opened.v1 notification 爭議受理 ❌ 無對應模板 ✅ notifications ✅ — 需裁定(模板)

4.1 SAD-002 §3 獨有之消費邊(不在 SAD-004 §5.2)

# 事件 消費者 依據 判定
23 party.account.created.v1 card SAD-002 §3.3「事件(訂閱)」 待生產端(payload ⬜ 未定義、未實作)
24 notification.sent.v1 party SAD-002 §3.15 需裁定(SAD-004 §5.2 未列;用途未載明)
25 merchant.settlement.submitted.v1 clearing SAD-002 §3.13 之「主要相依」暗示 需裁定(無明確「訂閱」記載)

⚠️ #23~#25 之存在本身即 CR-027 §5 #5(兩份清單不一致)之具體案例。
#5 未裁定前,不宜動工——否則實作範圍將隨清單認定而變。


5. 判定分類與分批計畫

批次 內容 條數 前置條件
C-1 payment.payment.posted.v1 → notification(PAYMENT_RECEIVED) 1 事件補可選 currency(本 CR 已執行)
C-2 statement.statement.issued.v1 → notification(STATEMENT_ISSUED) 1 同上(本 CR 已執行)
C-3 payload 缺口:#6(缺 mcc/merchantId)、#8(缺 authorizationId/holdId)、#10(缺對照) 3 需裁定「補欄位」或「改契約」
C-4 冗餘同步路徑:#2/#5/#11(+#1 部分) 3~4 架構裁定(見 §6)
C-5 語意/讀模型未定義:#1/#3/#7/#9/#13/#18/#20/#21 8 ✅ 已全數結案(CR-033)——實作 1 條(#3=#A-143)、裁定不做 7 條、待實作 0 條。★ 根因揭露:本批 8 條係依 CR-030 之前之 SAD-004 §5.2 盤點;CR-030 §2 #5 重寫權威清單時已移除/改判這些消費邊,惟未回寫本 CR → 形成幽靈待辦(見 CR-033 §3.1)
C-6 無對應模板:#17/#19/#22 3 需裁定「新增模板」或「不做」
C-7 範圍大:#4(參數熱更新,16 服務) 1 需先立規格 + 裁定範圍——⏸ CR-031 §3 裁定:設計先行、暫緩實作(實測 9 服務皆無參數快取 → 無快取可失效;且 SAD-004 §5.2 之「全體」實為可呼叫 product 之 9 服務,已更正)
C-8 待生產端:#15/#16/#23 3 生產端未實作(屬批次 A-4 之「payload 未定義」群)
C-9 清單權威未定:#24/#25 2 CR-027 §5 #5

6. ★ 架構性問題:事件 vs 同步呼叫之重複(本 CR 最重要之發現)

實測發現 3 條消費邊所描述的行為,已由既有的同步呼叫完成:

事件 消費者 文件所述用途 既有同步路徑 並存之後果
authorization.authorization.approved.v1 limit 佔用額度 AuthorizeUseCase 呼叫 limit-service 同步佔用 重複佔用(額度被扣兩次)
payment.payment.posted.v1 posting 繳款入帳 payment-service 呼叫 posting-service 同步入帳 重複入帳(帳務重複)
posting.transaction.posted.v1 statement 帳務事件 statement 結帳時同步查詢 posting 若事件另建投影,兩套真相

⚠️ 這不是「實作細節」,而是架構決策:
SAD-004 §5.2 之「主要消費者」欄係事件驅動願景之描述,惟現行實作已採同步呼叫
(SAD-002 §3.x 之「同步呼叫」欄即載明)。兩者並存必然重複套用。

三種可能方向(需 BEAM PMO 裁定):
(a) 事件為唯一路徑——移除同步呼叫,改由事件最終一致(改動大,且授權路徑需即時佔用,不建議);
(b) 同步為主、事件為輔——事件僅用於投影/通知(不重做同步已做之寫入)——
本 CR 之 C-1/C-2 即採此;SAD-004 §5.2 之「用途」欄須改寫以排除「重做寫入」之語意(建議);
(c) 逐案裁定——由 BEAM PMO 就 3 條各自定方向(最保守)。

建議採 (b):理由——① 授權/繳款之即時性要求使同步不可去;
② 事件驅動之價值在跨服務投影與通知,重做寫入並無增益且引入重複風險;
③ (b) 下 C-1/C-2 已可動工,且 #1/#5/#7/#9/#20/#21 之「用途未載明」問題
亦可一併以「事件不重做寫入」之原則消解(僅餘「投影什麼」之問題)。


7. C-1/C-2 之執行(本 CR 已動工)

7.1 語意來源(非臆造)

事件 模板(既有種子,V2__notification_templates_and_notifications.sql) 通道 所需變數
payment.payment.posted.v1 PAYMENT_RECEIVED SMS/EMAIL(皆 enabled=1) amount、currency
statement.statement.issued.v1 STATEMENT_ISSUED EMAIL(enabled=1) statementMonth、totalAmount、currency、paymentDueDate

模板為既有種子資料——本 CR 僅「把事件接到既有模板」,未新增任何模板(新增模板者列 C-6 待裁定)。

7.2 契約缺口之處理:補可選 currency

事件 現行 payload(SDD-006 §4.2) 模板需要 缺口
payment.payment.posted.v1 paymentId/accountId/amount/allocations[]/availableLimitAfter amount、currency 缺 currency
statement.statement.issued.v1 statementId/accountId/statementMonth/totalAmount/minimumDue/paymentDueDate statementMonth、totalAmount、currency、paymentDueDate 缺 currency

處置:依 SAD-004 §5.3「新增可選欄位 → 不升版」補為可選。

先例(本專案已有兩次同樣處置,皆已執行):

  • CR-027 §5 #11:approved.v1 擴充 cardNoMasked/merchantId(可選)——#A-125 執行
  • CR-027 §5 #14:limit.hold.created.v1 補 currency(可選)——#A-128 執行

⚠️ 與 #10 之關係:#10 即「payment.payment.posted.v1 缺 currency」,建議 (b) 補為可選。
本 CR 依 #10 之建議選項 (b) 執行(因 C-1 需要),並登記待 BEAM PMO 追認——
與 #14 之處理完全一致(該項亦為「依建議執行 + 登記待裁定」)。
statement.statement.issued.v1 為同族第三例,建議一併納入 #10 之裁定範圍。

7.3 實作與驗證

項 內容
規格先行 SCS-008 §7.2(payment 補可選 currency)/SCS-011 §7.2(statement 補可選 currency)/SCS-016 §7.11/§7.12(兩消費端)
生產端 PaymentEventFactory 補 currency(Payment.currency() 本即存在);StatementEventFactory 補 currency(Statement.currency() 本即存在)——皆非新資料
消費端 PaymentPostedNotificationService + StatementIssuedNotificationService + 對應 Listener(複用 #A-125 之形狀:冪等 → 亂序 → submit → markConsumed)
共用化 AuthApprovedNotificationPort → EventConsumptionPort(consumed_events 表本即通用,僅名稱綁 auth)——避免複製三份
真機驗證 觸發繳款/出帳 → 事件 → 通知建立;負樣本:同 eventId 重放不重複、sequenceNo 落後略過

⚠️ 語意僅為「通知被正確建立」——遞送通道仍為真阻塞(SCS-016 §8:唯一實作為本地 Log stand-in)。
與 #A-125 同一立場:不宣稱「客戶已收到」。


7.4 ★ C-5 之結案(CR-033,2026-10-02)

BEAM PMO 指示「先執行 C-5 其餘 8 條目」 → 逐條查證後結案,詳見 CR-033。

# 事件 → 消費者 結案
1 authorization.approved.v1 → posting 不做(CR-030 §4.1:同步已做)
3 card.status-changed.v1 → party ✅ 已完成(#A-143/CR-031 §2)
7 posting.posted.v1 → clearing 不做(CR-030 §4.1:批次對帳)
9 posting.reversed.v1 → statement 不做(CR-030 §4.1:結帳同步查詢)
13 statement.issued.v1 → payment 不做(非事件消費邊;「自扣」屬未實作功能)
18 rewards.points.redeemed.v1 → posting 不做(SDD-003 §13 為同步呼叫,#16 不得重做)
20 clearing.batch.matched.v1 → posting 不做(§5.2 已標「不做消費端」;阻塞為會計科目設計)
21 dispute.case.opened.v1 → posting 不做(非事件消費邊;帳務調整屬結案路徑)

→ 實作 1 條、裁定不做 7 條、待實作 0 條。

★★ 根因:本批係「幽靈待辦」

C-5 之 8 條係依 CR-030 之前之 SAD-004 §5.2 盤點而得;
CR-030 §2 #5 將該清單重寫為唯一權威時已移除/改判這些消費邊,
惟未回寫本 CR → 本批仍顯示為待辦(詳 CR-033 §3.1,含 git show 之新舊清單對照)。

教訓:重寫權威清單後,必須回寫「以舊清單為基礎之盤點」——否則產生幽靈待辦。


8. 請求裁定事項(給 BEAM PMO)

  1. §6 架構方向:建議採 (b)「同步為主、事件為輔」——事件僅供投影/通知,不重做同步已做之寫入。
    此為批次 C 能否大規模推進之前提。
  2. §5 C-3 payload 缺口:posting.posted.v1 是否補 authorizationId/mcc/merchantId(可選)?
    (#8 之「釋放佔用」若採 (b) 且「不重做寫入」,則此條可降級為不做——請一併裁定。)
  3. §5 C-6 模板:rewards.points.*/dispute.case.opened 之通知是否要做?
    若要做,模板由誰提供(新增種子模板屬「新增業務文案」,不應由開發自行編寫)。
  4. CR-027 §5 #10 之追認:本 CR 已依建議 (b) 為 payment.posted.v1 及 statement.issued.v1
    補可選 currency(不升版);請追認,或指示改為必填/升版。
  5. CR-027 §5 #5(清單權威)與 #6(q_* 語意):此二項直接決定批次 C 之範圍
    (#5 決定「哪些消費邊存在」;#6 決定「自投影是否要做」)→ 請優先裁定。

9. 修訂記錄

版本 日期 說明
1.2 2026-10-02 C-5 全數結案(CR-033)——BEAM PMO 指示「先執行 C-5 其餘 8 條目」。逐條查證後結論:實作 1 條(#3=#A-143)、裁定不做 7 條、待實作 0 條。★★ 揭露根因:本批 8 條係依 CR-030 之前之 SAD-004 §5.2 盤點;CR-030 §2 #5 重寫權威清單時已移除/改判這些消費邊,惟未回寫本 CR → 形成幽靈待辦(已以 git show 9937bd0 取新舊清單對照為證)。新增 §7.4 結案表;§5 之 C-5 列與檔頭狀態同步更新
1.1 2026-10-01 依 CR-031(批次 C 剩餘項之裁定)更新 §5:C-5 首項落地(party 卡片狀態投影,SCS-004 §7.5,真機驗證通過)、C-7 裁定暫緩(實測 9 服務皆無參數快取);C-5 其餘條目仍待逐條立讀模型規格
1.0 2026-10-01 初版:批次 C(消費端)盤點——SAD-004 §5.2 22 條 + SAD-002 §3 獨有 3 條,逐條判定「語意來源/目標模型/payload/同步冗餘」;排除 q_*(7/7 自投影);★ 揭露架構性問題(事件 vs 同步呼叫重複,3 條);C-1/C-2 動工(語意來源=既有種子模板;依 SAD-004 §5.3 補可選 currency);其餘分為 C-3~C-9 共 9 批待裁定
    (1-1/1)