CR #228 » CR-027_事件流W-2推廣盤點與排序建議.md
CR-027 事件流(W-2)推廣盤點與排序建議
| 項目 | 內容 |
|---|---|
| 文件編號 | CR-027 |
| 版本 | 1.8 |
| 提出日 | 2026-10-01 |
| 提出者 | WorkBuddy AI(開發團隊) |
| 緣起 | BEAM PMO 指示「① 其餘 15 個事件的生產/消費(比照本模式逐條打通)」。前 3 條已打通(#A-117~#A-121)後,動工前查證發現 8 項文件互相矛盾/缺口——盲目照做會做出語意錯誤的事件流,故先盤點並請求裁定。1.1:批次 A-1(再發現 2 項)。1.2:批次 A-2/A-3(再發現 2 項)——批次 A 全數完成。1.3:#1~#4(hold 群)已化解(CR-028);#13 新增。1.4:批次 B 完成(#A-128);#14 新增。1.5:批次 A-4 完成(#A-133~#A-138)——payload 已定義之 9 項事件全數打通;#15 新增(主題命名不一致);併更正 #A-131 之根因。1.6:批次 C 動工——CR-029 新建(消費端盤點與讀模型設計建議);C-1/C-2 完成;#16 新增(事件 vs 同步呼叫之重複)。1.7:§5 十二項全數裁定並執行完畢(CR-030)。1.8:批次 C 剩餘項——C-5 首項完成(party 卡片狀態投影,架構上唯一解;CR-031);C-7 裁定暫緩(實測 9 服務皆無參數快取) |
| 影響範圍 |
SAD-002 §3、SAD-003 §4、SAD-004 §5.2/§5.3/§5.4、SDD-006 §4.2;GOV-006 §4.8
|
| 狀態 |
批次 A ✅、批次 B ✅、批次 A-4 ✅、批次 C 🟡;★ §5 之 #5~#16(十二項)已全數裁定(CR-030)並執行完畢;★ 批次 C 之 C-1/C-2/C-5 首項已完成(#A-141/#A-143)、C-7 裁定暫緩(CR-031 §3);餘 12 項事件之 payload 未定義(需先立契約)、C-5 其餘 8 條目仍待立讀模型規格
|
1. 現況(已完成)
| # | 事件 | 生產端 | 消費端 | 狀態 |
|---|---|---|---|---|
| 1 | card.card.status-changed.v1 |
card-service ✅ | authorization-service ✅ | ✅ 全鏈路(#A-117+#A-118) |
| 2 | limit.limit.adjusted.v1 |
limit-service ✅ | authorization-service ✅ | ✅ 全鏈路(#A-119) |
| 3 | authorization.authorization.approved.v1 |
authorization-service ✅ |
notification-service ✅(#A-125);posting/limit ⬜ |
✅ 全鏈路(至少一消費端)(#A-121+#A-125) |
| 4 |
limit.hold.created.v1(不在 SAD-004 §5.2) |
limit-service ✅ |
authorization-service ✅(#A-128) |
✅ 全鏈路(#A-122+#A-128) |
| 5 | limit.hold.released.v1 |
limit-service ✅ |
authorization-service ✅(#A-128) |
✅ 全鏈路(#A-122+#A-128) |
| 6 | payment.payment.posted.v1 |
payment-service ✅ | limit-service ✅ | ✅ 全鏈路(#A-124;CR-027 批次 A-1) |
| 7 |
card.card.issued.v1(不在 SAD-004 §5.2) |
card-service ✅ | (無) | 🟡 僅生產端(#A-126) |
| 8 |
card.token.bound.v1(不在 SAD-004 §5.2) |
card-service ✅ | (無) | 🟡 僅生產端(#A-126) |
🔧 補充:
limit.hold.created.v1(第 4 列)亦已於#A-128完成消費端(auth 本地limit_holds投影)→ 全鏈路。
2. 兩份「權威清單」不一致(本 CR 之核心)
盤點發現專案內有兩份事件清單,且不一致:
| 來源 | 性質 | 事件數 |
|---|---|---|
SAD-002 §3(各服務之「事件(發布)/(訂閱)」) |
服務 × 事件矩陣(含訂閱關係) | 約 30 |
SAD-004 §5.2(事件清單(主要)) |
主要事件表(含主要消費者) | 16 |
⚠️ 兩份皆自稱權威(
SAD-004為「事件治理」章、SAD-002為「微服務劃分」章),
惟內容不同:SAD-002有party.*/card.card.issued/card.token.bound/posting.balance.changed
等,SAD-004 §5.2皆無;反之SAD-004 §5.2有dispute.case.opened.v1,SAD-002 §3.9~3.17則以摘要形式列出。
本 CR 不擅自擇一(見 §5 待裁定 #5)。
3. 生產端就緒度(可線性推進,風險低)
| 服務 | 應發布事件 | 生產端掛點(程式) | 契約 payload | 就緒 |
|---|---|---|---|---|
| authorization |
…approved.v1 ✅/…declined.v1
|
AuthorizeUseCase(已接 outbox) |
SDD-006 §4.2 ✅ |
🟢 易 |
| limit |
limit.hold.created.v1/limit.hold.released.v1/limit.special-limit.set.v1
|
LimitHoldService(已接 outbox) |
✅ | 🟢 易 |
| card |
card.card.issued.v1/card.token.bound.v1
|
CardQueryService(已接 outbox) |
✅ | 🟢 易 |
| party |
party.account.created.v1/party.block-code.changed.v1/party.flag.applied.v1
|
需接 outbox(尚無) | ✅ | 🟡 中 |
| product |
product.parameter.changed.v1/product.product.published.v1
|
需接 outbox | ✅ | 🟡 中 |
| posting |
posting.transaction.posted.v1/…reversed.v1/posting.balance.changed.v1
|
需接 outbox | ✅ | 🟡 中 |
| payment |
payment.payment.posted.v1/…reversed.v1
|
需接 outbox | ✅ | 🟡 中 |
| statement | statement.statement.issued.v1 |
需接 outbox | ✅ | 🟡 中 |
| installment |
installment.plan.created.v1/…period.settled.v1
|
需接 outbox | ✅ | 🟡 中 |
| rewards |
rewards.points.accrued.v1/…redeemed.v1
|
需接 outbox | ✅ | 🟡 中 |
| clearing | clearing.batch.matched.v1 |
需接 outbox | ✅ | 🟡 中 |
| dispute |
dispute.case.opened.v1/…closed.v1
|
需接 outbox | ✅ | 🟡 中 |
| merchant | merchant.settlement.submitted.v1 |
需接 outbox | ✅ | 🟡 中 |
結論:生產端 13 服務皆可做——掛點明確、
SDD-006 §4.2之 payload 已定義、模式已驗證 3 次。
成本主要在每個服務要各建一次 outbox 表 + relay(V?_outbox.sql+ 6 個類別),屬複製型工作。
4. 消費端就緒度(真正的難點)
判準:消費端是否已有「該被餵養的表」。沒有的話,貿然建表=無中生有(
CSP-001 §8 B2/假綠風險)。
| 事件 | 消費者(SAD-004 §5.2) |
消費端表 | 就緒 |
|---|---|---|---|
authorization…approved.v1 |
posting/limit/notification | posting:無「待請款」表;limit:account_limits.used_amount(有欄位);notification:notifications(有表) |
🟡 部分可做 |
authorization.hold.created.v1 |
limit | limit 之 limit_holds 是權威,不應由 auth 覆寫 |
⛔ 語意矛盾(§5 #1) |
limit.hold.created.v1 |
authorization(SAD-002) |
auth 之 limit_holds(本地投影,無餵養者) |
🟡 有表,但 payload 缺 authorizationId(§5 #4) |
limit.hold.released.v1 |
authorization | 同上 | 🟡 同上 |
product.parameter.changed.v1 |
全體 | 無參數快取表 | ⛔ 需新表+17 服務 |
posting.transaction.posted.v1 |
statement/rewards/clearing/limit | limit:account_limits.used_amount;其餘多為權威表自身
|
🟡 部分 |
posting.transaction.reversed.v1 |
statement/rewards | 同上 | 🟡 部分 |
payment.payment.posted.v1 |
limit/posting/notification | limit:account_limits.used_amount(恢復額度,語意明確) |
🟢 可做 |
statement.statement.issued.v1 |
payment/notification | payment:無帳單表 | ⛔ 需新表 |
installment.plan.created.v1 |
limit/posting | limit:分期佔用(無對應表) | ⛔ 需新表 |
rewards.points.accrued.v1 |
notification |
notifications(有表) |
🟡 但發送通道為真阻塞 |
rewards.points.redeemed.v1 |
posting/notification | posting:無點數兌換入帳表 | ⛔ |
clearing.batch.matched.v1 |
posting | 無對應表 | ⛔ |
dispute.case.opened.v1 |
posting/notification | 無對應表 | ⛔ |
結論:消費端僅 2~3 條可立即做;其餘需先立讀模型規格(=先決定「這張表是什麼」),
否則就是為打通而造表——正是客戶硬性要求禁止的「塞框架/mock data」精神。
5. ★ 待裁定(十六項)——動工前必須先定
✅ 本節 12 項已全數裁定(2026-10-01,
CR-030)BEAM PMO 2026-10-01 指示:「以 Best Practice(Payment Industry 20 年金融科技/架構師)直接將這些問題解決」。
→ 已立CR-030《事件流治理:CR-027 §5十二項待裁定之架構裁定》,以 4 條業界通則(P1~P4)
為方法論,逐項裁定並載明「理由/業界依據/影響面/可逆性」。本節下表保留為決策歷程。
# 爭點 裁定 執行狀態 5 事件清單權威 (c) SAD-004 §5.2為唯一權威;SAD-002 §3降為指標✅ 文件 6 q_*語意廢除 7 個自投影;改立「跨服務投影清單」 ✅ 文件 7 declined.v1不納入事件流;改以 metrics 涵蓋 ✅ 文件 + 程式 8 REFERRAL缺漏★ approved.v1語意=「已佔用額度」;補decision;發射條件改holdsLimit()✅ 程式(真缺陷) 9 UNMATCHED/REVERSED★ 補發射 + limit重新佔用(超額風險之真缺陷)✅ 程式(真缺陷) 10 posted.v1缺currency確認為可選(已執行)+ 提升為通則(凡涉金額必帶) ✅ 文件(追認) 11 approved.v1擴充 2 欄追認( cardNoMasked為遮罩,符合 PCI)✅ 文件(追認) 12 sequenceNo交錯明訂不變式「同聚合之所有事件型別共用同一序號來源」;修正 card✅ 文件 + 程式 13 card.status-changedpayload追認以實作為準 ✅ 文件(追認) 14 limit.hold.created補currency追認 ✅ 文件(追認) 15 主題命名不一致 (a) 對齊文件慣例;修正 product/clearing/dispute/posting✅ 文件 + 程式 16 事件 vs 同步呼叫 (b)「同步為主、事件為輔」;事件不重做同步已做之寫入 ✅ 文件 + 連帶收斂批次 C 範圍 另 4 項留待外部輸入(
CR-030 §4.2,不得由開發臆造):業務文案(通知模板)/
卡組織規範(拒付時限)/聯絡資訊端點/累點規則。以下為決策歷程原文(保留供追溯):
✅ #1~#4(hold 事件群)已化解(2026-10-01,
CR-028)——無需 BEAM PMO 裁定:
經取三項客觀證據(authorization.hold.*全庫程式碼零引用/其 payload 與limit.hold.created.v1
逐欄相同/hold 之權威在CC_LIM.LIMIT_HOLDS),確認為「同一事實兩個生產者」:
(#1) 廢除authorization.hold.*,hold 事件一律由 limit 發布;
(#2/#3)authorization-service同時訂閱limit.hold.created.v1與limit.hold.released.v1;
(#4)limit.hold.created.v1補authorizationId為可選欄位(不升版)。
程式碼零影響;已執行 6 份文件之同步修訂(詳CR-028 §4)。
→ 批次 B 之規格障礙已解除(藍圖見CR-028 §6)。下列原表保留作為決策歷程(爭點與選項),現況以本 blockquote 為準。
| # | 爭點 | 衝突內容 | 選項 | 建議 |
|---|---|---|---|---|
| 1 | hold 事件之方向與權威 |
SAD-002 §3.1 說 authorization 發布 authorization.hold.created.v1/.released.v1;SAD-002 §3.2 又說 limit 發布 limit.hold.created.v1/.released.v1。而 SAD-002 §3.2/SDD-001 明訂 hold 之權威在 CC_LIM.limit_holds
|
(a) 廢 authorization.hold.*(只留 limit.hold.*)(b) 保留,但定義為「本地代佔(stand-in)」事件 (c) 兩者並存,各自語意於 SAD-004 補明 |
(a)——權威只有一個;authorization.hold.* 實為命令(「請你佔用」),不該是事件。若採 (b) 須補 payload 與消費者 |
| 2 | authorization 訂閱清單 |
SAD-002 §3.1 訂閱 limit.hold.created.v1;SAD-004 §5.2 無此事件(只有 limit.hold.released.v1) |
(a) 以 SAD-002 為準(補進 SAD-004)(b) 以 SAD-004 為準(修 SAD-002) |
(a)——auth 需要 hold 的建立才知本地投影該有幾列;只給 released 會「釋放一筆不存在的佔用」 |
| 3 | limit.hold.released.v1 之消費者 |
SAD-004 §5.2 消費者=authorization;SAD-002 §3.1 之 auth 訂閱清單未含
|
同上 | 補進 SAD-002(與 #2 同批處理) |
| 4 | limit.hold.created.v1 payload 缺 authorizationId |
SDD-006 §4.2 為 holdId, accountId, holdAmount, holdType——無 authorizationId,auth 無法把 hold 對應回自己的授權(limit_holds.authorization_id) |
(a) 補 authorizationId(新增必填 → 依 SAD-004 §5.3 須升 v2 + 雙軌 ≥1 release)(b) 改為選填(不升版) (c) auth 端以 holdId 反查(需另存對照,不建議) |
(b)——authorizationId 為可選(stand-in 路徑無對應授權時可空),依 SAD-004 §5.3「新增可選欄位不升版」 |
| 5 | 事件清單以哪份為權威 |
SAD-002 §3(~30 事件)vs SAD-004 §5.2(16 事件)不一致
|
(a) SAD-002 為權威(服務矩陣),SAD-004 §5.2 降為「主要事件節錄」(b) SAD-004 §5.2 為權威,逐服務回寫 SAD-002(c) 合併為單一清單(放 SAD-004,SAD-002 僅留指標) |
(c)——「單一真實來源」是本案鐵律;兩份並存必然再漂移 |
| 6 | SAD-003 §4.1 之 7 個 q_* 讀模型語意 |
多數與該服務自身權威表同名同義(q_transaction_history vs posting.transactions、q_authorization_history vs authorization.authorizations、q_available_limit vs limit.account_limits…) |
(a) 視為「服務內自投影」(寫模型→事件→讀模型) (b) 視為「跨服務投影」( SAD-003 表頭之「服務」欄為存放地)(c) 廢除本表 |
(b) 或 (c)——若是自投影,實作價值低且與權威表混淆(本專案已有 card_status_caches 之「本地投影」慣例,語意清楚);建議明確改寫為跨服務投影,否則廢除並以「各服務之 CQRS 讀模型清單」取代 |
| 7 | authorization.authorization.declined.v1 是否納入 |
SDD-006 §4.2 有定義、SDD-003 時序圖有寫、SAD-002 §3.1 有列;惟 SAD-004 §5.2「主要事件」未列
|
(a) 納入(拒絕亦需下游得知?) (b) 不納入(無消費者) |
(b)——目前找不到消費者(拒絕不佔額、不產生待請款)。若為稽核用途,應走 audit_trail 而非事件流。但若客戶要求「授權拒絕率即時看板」則需 (a)
|
| 8 |
REFERRAL(轉人工)事件缺漏(#A-121 實測發現,已記 SCS-001 §10 #8) |
REFERRAL 已佔用額度,卻不發射任何事件(AuthDecision.isApproved() 不含 REFERRAL)→ 下游無從得知該筆佔用 |
(a) 另立 authorization.authorization.referred.v1(b) 讓 approved.v1 涵蓋 REFERRAL(以 standinFlag/新增 decision 欄位區分) |
(b)——轉人工件確實佔了額度,語意上屬「已核准佔用、待人工覆核」;另立事件會讓下游要多接一條而語意重疊。建議 payload 補 decision(可選),讓下游可分辨 |
| 9 |
UNMATCHED/REVERSED 之事件未實作(#A-124 實作 A-1 時發現,已記 SCS-008 §10 #11) |
SCS-008 §8.2 轉換 6 會把 LIMIT_RESTORED 設回 false(撤銷額度恢復=重新佔額)、§7 沖正亦然;惟 §7.1 只發射 posted.v1 → 「繳款後又轉未匹配/沖正」時,額度不會被重新佔用
|
(a) 補 payment.payment.unmatched.v1(SDD-006 §4.2 已定義;惟不在 SAD-004 §5.2)+ payment.payment.reversed.v1(SAD-002 §3.7 有列)(b) 讓 posted.v1 之 payload 帶 limitRestored,消費者據以「恢復/撤銷」(c) 暫不處理,記為已知限制 |
(a)——兩個語意不同(「繳款入帳」vs「重新佔額」),用同一事件帶旗標會讓消費者承擔狀態機責任。⚠️ 惟 (a) 需先解決 #5(清單權威),否則又是不在清單內的事件 |
| 10 |
payment.payment.posted.v1 payload 缺 currency(#A-124 實作 A-1 時發現,已記 SCS-008 §10 #12) |
SDD-006 §4.2 之欄位無 currency,惟 payments.currency 存在;消費端(limit-service)之額度主檔鍵為 (account_id, currency) → 無法精確定位
|
(a) 補 currency 為必填(升 v2,雙軌 ≥1 release)(b) 補為可選(不升版) (c) 不改契約,消費端以「該帳戶恰一列才更新」之保守規則處理 |
(b)——依 SAD-004 §5.3「新增可選欄位不升版」;消費端已以 (c) 之保守規則實作(多幣別時告警不更新,不臆測)。★ 2026-10-01 更新( CR-029 批次 C-1/C-2):已依 (b) 執行——payment.posted.v1 及 statement.statement.issued.v1 皆補可選 currency(不升版;SCS-008 §7.2/SCS-011 §7.2)。理由:notification-service 之既有種子模板 PAYMENT_RECEIVED/STATEMENT_ISSUED 需要該欄,否則無法建立通知(CC-NOT-1003)。請追認(比照 #11/#14 之「依建議執行 + 登記待裁定」) |
| 11 |
authorization.authorization.approved.v1 payload 已擴充 2 個可選欄位(#A-125 實作 A-2 時主動擴充,已記 SCS-001 §7.14/SCS-016 §10 #10) |
AUTH_APPROVED 樣板需要 cardLast4/merchant,惟原 payload 未帶 cardNoMasked/merchantId → 通知無法建立(變數不足) |
(a) 接受本次擴充(可選欄位,依 SAD-004 §5.3 不升版)(b) 改為必填並升 v2 雙軌(c) 撤回,改由消費端容忍缺欄(以空值渲染) |
(a)——兩欄皆非敏感(cardNoMasked 為遮罩,先例見 card.card.status-changed.v1;merchantId 為商戶代號)。⚠️ 惟須留意:擴充前之舊事件將因變數不足而略過(不會補出通知)——重放歷史事件時需知悉 |
| 12 |
card.card.issued.v1/token.bound.v1 之 sequenceNo 與 status-changed 不同源(#A-126 實作 A-3 時發現,已記 SCS-003 §10 #19) |
前者用同 JVM 內遞增之時間戳基底序號,後者用卡片版本;惟二者 aggregateId 同為 cardId → 同一聚合之不同事件型別可能序號交錯
|
(a) 統一為卡片版本推導(cardId 層級)(b) 改為聚合層級持久化序號(每個 aggregateId 一計數器)(c) 明訂「序號僅在 (aggregateId, eventType) 內有意義」,消費者據此判準 |
(b) 為正解(SAD-004 §5.4 要求「同一聚合內依 sequenceNo 遞增」);(c) 為立即可行之權宜——惟須同步修 SAD-004 §5.4,避免與現行文字衝突 |
| 13 |
SDD-006 §4.2 之 card.card.status-changed.v1 payload 與實作不符(CR-028 執行時發現) |
契約表載 cardId, fromBlockCode, toBlockCode, reason(狀態變更之差量);惟實作(SCS-003 §7.10,#A-118 真機驗證)發射者為變更後之完整狀態(cardNoHash, cardId, accountId, cardNoMasked, status, blockCode, expiryDate) |
(a) 依實作更正契約表(已於 CR-028 執行)(b) 改實作以符合原契約(破壞性:已發布之事件形狀改變,且 SCS-003 §7.10/消費端皆須改) |
(a)——實作已真機驗證並有消費端(card_status_caches);原契約表為該事件之唯一錯誤記載。惟依 CSP-003 §2 精神,改契約須經覆核 → 列此待 BEAM PMO 確認
|
| 14 |
limit.hold.created.v1 payload 缺 currency(批次 B 實作時發現) |
CC_AUTH.LIMIT_HOLDS.currency 為 NOT NULL(availableLimit 之查詢亦以幣別過濾),惟事件原 payload 未帶 currency → 消費端無法寫入本地投影
|
(a) 補 currency 為可選(已於批次 B 執行;依 SAD-004 §5.3 不升版)(b) 補為必填(升 v2 雙軌)(c) 消費端一律回查 account_limit_caches
|
(a)——limit-service 本來就有該值(hold() 之入參),且已實作回查 fallback(事件未帶時回查,恰一列才用)。⚠️ 與 #10(posted.v1 缺 currency)同型——建議一併裁定 |
| 15 | ★ Kafka 主題命名不一致(本批實測發現,2026-10-01) |
OutboxPublisher.topicFor 為 cc.<aggregateType>.<eventType>;SAD-004 §5.4 之示例與 SCS-002 §7.5/SCS-003 §7.11 之既有記載皆為**「事件全名去版本後、再移除一層與聚合同名之首段」(card.card.issued.v1 → card.issued → 主題 cc.card.card.issued)。惟後期 4 個服務寫入完整名** → 主題多一層重複:product.parameter.changed → cc.product.parameter.changed;clearing.batch.matched → cc.clearing.batch.matched;dispute.case.opened → cc.dispute.case.opened;posting.transaction.posted → cc.posting.transaction.posted
|
(a) 對齊文件慣例(4 服務各改 1 個常數 → 主題變為 cc.product.parameter.changed/cc.clearing.batch.matched/cc.dispute.case.opened/cc.posting.transaction.posted)(b) 改文件以承認「完整名」為準(則 card/limit/auth 三服務需改,且主題將出現三層如 cc.card.card.card.issued) |
(a)——①現階段零消費者(批次 C 未動工),變更成本=每服務 1 行 + 重驗;②文件(SAD-004 §5.4 示例 + 兩份 SCS 既有記載)為多數且較早之慣例;③**(b) 會使主題退化為三層**(cc.card.card.card.issued),明顯非設計意圖。★ 本批之 statement/rewards/merchant/notification 已依 (a) 實作(cc.statement.statement.issued/cc.rewards.points.accrued/cc.merchant.settlement.submitted/cc.notification.sent)→ 目前新舊不一致,建議裁定後一次對齊 |
★ 附註(
#A-131根因更正,2026-10-01):#A-131原記「clearing之端到端受阻於服務間 ACL」,
經複查更正——本機ops/local/sso.env為CARDCORE_SECURITY_ENABLED=true(SSO 開啟),
故未帶權杖即回401 CC-PLT-4010(未認證);ACL 之拒絕碼為403 CC-PLT-4030,二者不同。
以合法使用者權杖(demo.csr/clientcardcore-services,azp非cc-*→ 不受服務間 ACL 管制)
重跑即 201、outbox 落列、主題建立。→ 非阻塞,clearing已於本批完成驗證。
| 16 | ★ 事件 vs 同步呼叫之重複(CR-029 批次 C 盤點發現,2026-10-01) | 三條消費邊所描述的行為已由既有同步呼叫完成:① authorization.approved.v1 → limit(佔用額度)vs AuthorizeUseCase 同步呼叫 limit 佔用;② payment.payment.posted.v1 → posting(繳款入帳)vs payment 同步呼叫 posting 入帳;③ posting.transaction.posted.v1 → statement(帳務事件)vs statement 結帳時同步查詢 posting。兩者並存必然重複套用(重複佔用/重複入帳/兩套真相) | (a) 事件為唯一路徑——移除同步呼叫(授權需即時佔用,不建議)
(b) 同步為主、事件為輔——事件僅供投影/通知,不重做同步已做之寫入;SAD-004 §5.2 之「用途」欄須改寫
(c) 逐案裁定 | (b)——① 授權/繳款之即時性使同步不可去;② 事件驅動之價值在跨服務投影與通知,重做寫入無增益且引入重複風險;③ (b) 下批次 C 始可大規模推進(C-1/C-2 即採此)。此為批次 C 之最大前提,請優先裁定(詳 CR-029 §6/§8.1) |
6. 建議排序(依「可驗證性」而非「事件編號」)
批次 A|無爭議、可立即做(不依賴 §5 任何裁定)
| 順序 | 內容 | 消費者就緒 | 真機驗證方式 |
|---|---|---|---|
| A-1 |
payment.payment.posted.v1(payment 生產)→ limit-service 消費:恢復可用額度#A-124,2026-10-01)
|
🟢 有 | ✅ 繳款入帳 → account_limits.used_amount 500,000 → 300,000;冪等/亂序/溢繳下限皆通過(見 SCS-002 §7.7) |
| A-2 |
authorization…approved.v1 之消費端:notification-service 建立通知#A-125,2026-10-01)
|
🟢 有 | ✅ 授權核准 → 通知 NOTIF202610000003 建立(AUTH_APPROVED、SENT、4 通道);冪等/亂序皆通過(見 SCS-016 §7.9) |
| A-3 |
card.card.issued/token.bound——已接 outbox 之服務,補事件即可)#A-126,2026-10-01)
|
— | ✅ 發卡 → cc.card.card.issued;綁 Token → cc.card.card.token.bound;payload 與 SDD-006 §4.2 逐欄一致(見 SCS-003 §7.11/§7.12) |
批次 A 三項全數完成(A-1
#A-124/A-2#A-125/A-3#A-126,2026-10-01)。
實作中共發現 4 項契約缺口(§5 #9~#12),已一併登錄。
下一步:批次 B(hold 群)動工前必須先裁定 §5 #1~#4。
批次 B|✅ 已完成(2026-10-01,#A-128)
limit.hold.created.v1/limit.hold.released.v1 → auth 本地 limit_holds 投影
(V2 已載明之第三個「無餵養者」讀模型)。
真機:正常路徑 → 本地 8 → 9(id=權威 holdId);stand-in 去重負樣本 → 本地仍 1 列。
★ 併修正 JdbcLimitPort.availableLimit 之超額風險缺陷。
批次 A-4|生產端補齊(續)——BEAM PMO 2026-10-01 指示「先補批次 A 剩下的生產端」
範圍:批次 A-3 只補了「已接 outbox 之服務」(
card/limit)。本批補其餘服務之生產端。
掃描結果(2026-10-01 實測):
| 類 | 服務 | outbox 表/埠/工廠 |
|---|---|---|
| 已接(4) |
authorization/card/limit/payment
|
✅/✅/✅ |
| 未接(13) |
clearing/dispute/installment/merchant/migration/notification/party/posting/pricing/product/report/rewards/statement
|
—/—/— |
待補事件清單(依 SAD-002 §3):
| # | 服務 | 事件 | payload(SDD-006 §4.2) |
備註 |
|---|---|---|---|---|
| 1 | product |
product.parameter.changed.v1 |
✅ 已定義(namespace/paramKey/effectiveFrom) |
✅ 已完成(#A-130)——真機驗證通過;消費端(全體服務)未實作
|
| 2 | product |
product.product.published.v1 |
⬜ 缺 | 需先立 payload |
| 3 | party |
party.account.created.v1/party.block-code.changed.v1/party.flag.applied.v1
|
⬜ 缺(3 者皆缺) | 需先立 payload |
| 4 | posting |
posting.transaction.posted.v1 |
✅ 已定義(含 subAccountEntries[]) |
✅ 已完成(#A-134)——真機驗證通過(入帳 → cc.posting.transaction.posted) |
| 5 | posting |
posting.transaction.reversed.v1 |
✅ 已定義 | ✅ 已完成(#A-134)——真機驗證通過(沖正 → cc.posting.transaction.reversed) |
| 6 | posting |
posting.balance.changed.v1 |
⬜ 缺 | 需先立 payload |
| 7 | payment |
payment.payment.reversed.v1 |
⬜ 缺 | payment 已接 outbox,僅需補事件 |
| 8 | limit |
limit.special-limit.set.v1 |
⬜ 缺 | ⚠️ 無生產端程式點——SCS-002 §10 #2 載「SPECIAL 調整類型未支援」→ 需先實作該功能
|
| 9 | statement |
statement.statement.issued.v1 |
✅ 已定義 | ✅ 已完成(#A-135)——真機驗證通過(出帳 → cc.statement.statement.issued;冪等重跑不重複發射) |
| 10 | installment |
installment.plan.created.v1 |
✅ 已定義 | 掛點:建立分期 |
| 11 | installment |
installment.period.settled.v1 |
⬜ 缺 | 需先立 payload |
| 12 | rewards |
rewards.points.accrued.v1/rewards.points.redeemed.v1
|
✅ 已定義(2 者) | ✅ 已完成(#A-136)——真機驗證通過(入點/兌換 → 兩主題);併負樣本:退款(ADJUST+負值)不發射、冪等重播不重複發射
|
| 13 | clearing |
clearing.batch.matched.v1 |
✅ 已定義 | ✅ 已完成(#A-131 實作 + 本批補驗)——真機驗證通過(cc.clearing.batch.matched)。⚠️ #A-131 原記「受 ACL 阻擋」之根因已更正:實為本機 SSO 開啟(CARDCORE_SECURITY_ENABLED=true)→ 無權杖即 401 CC-PLT-4010(非 ACL 之 403)。見 §5 #15 之附註 |
| 14 | dispute |
dispute.case.opened.v1 |
✅ 已定義 | ✅ 已完成(#A-133)——真機驗證通過(cc.dispute.case.opened)。★ 實作時由單測攔下一項語意缺陷:disputeType 原誤取 DISPUTE_TYPE(程序類型,恆為 INQUIRY),應取 DISPUTE_REASON(爭議原因) |
| 15 | dispute |
dispute.case.closed.v1 |
✅ 已定義 | ⛔ 不發射:payload 含 resolution/adjustAmount,惟 Dispute 聚合無該二欄位且無結案路徑可達 → 不臆造(見 SCS-012 §10 #8) |
| 16 | merchant |
merchant.settlement.submitted.v1 |
✅ 已定義 | ✅ 已完成(#A-137)——真機驗證通過(cc.merchant.settlement.submitted);併負樣本:冪等重播不發射、淨額為負之 EXCEPTION 仍發射
|
| 17 | merchant |
merchant.merchant.registered.v1 |
⬜ 缺 | 需先立 payload |
| 18 | notification |
notification.sent.v1 |
✅ 已定義 | ✅ 已完成(#A-138)——真機驗證通過(cc.notification.sent);併負樣本:QUEUED/FAILED/受理階段皆不發射
|
| 19 | report |
report.job.completed.v1 |
⬜ 缺 | 需先立 payload |
| 20 | migration |
migration.job.completed.v1 |
⬜ 缺 | 需先立 payload |
| 21 | pricing |
pricing.interest.calculated.v1 |
⬜ 缺 | 需先立 payload |
批次 A-4 進度小結(2026-10-01):payload 已定義者(8 項)全數完成——
posting×2(#A-134)/statement(#A-135)/rewards×2(#A-136)/clearing(#A-131+補驗)/
dispute(#A-133)/merchant(#A-137)/notification(#A-138);
另加 product(#A-130)= 9 項事件/7 服務。
餘 12 項皆為「payload 未定義」(需先立契約)+ dispute.case.closed.v1(待裁定)。
每個服務之工作量(複製型):V?_outbox.sql + OutboxPort + <X>EventFactory +
JdbcOutboxRepository + {KafkaEventPublisher, OutboxRelayConfig, OutboxRelayScheduler} +
掛點(同交易寫 outbox)+ @EnableScheduling + application.yml 值序列化改 StringSerializer + 單測 + 真機。
⚠️ 順序建議:先做 payload 已定義者(免立規格);
limit.special-limit.set.v1最後做
——它缺的不只是事件,是整個SPECIAL調整功能(SCS-002 §10 #2),屬功能開發而非事件推廣。⚠️ 與 §7「不建議一次補完生產端」之張力(誠實揭露):§7 之顧慮是「有人發、沒人收」。
BEAM PMO 2026-10-01 裁定「先補生產端,再批次 C」——本 CR 依此執行,但每一批仍須真機驗證
(outbox 落列 + relay 發布 + 主題建立),並在規格如實標記「目前無消費者」,避免誤認為已全鏈路。
批次 C|消費端 → ★ 已立計劃(CR-029)
2026-10-01 更新:本批之盤點與分批計畫已獨立為
CR-029
(《事件流(W-2)批次 C:消費端盤點與讀模型設計建議》)——原因:本批之障礙不是技術,
而是**「目標讀模型/行為尚未被規格定義」**,需逐條判定而非照單全做。
CR-029之結論(摘要):
SAD-004 §5.2之 22 條消費邊 +SAD-002 §3獨有 3 條,逐條判定「語意來源/目標模型/payload/同步冗餘」。SAD-003 §4.1之 7 個q_*全部是「自投影」(來源事件與存放服務同一)→ 不在批次 C 之範圍(屬§5 #6)。- 僅 2 條可立即動工(
C-1/C-2:payment.posted/statement.issued→ notification)
——其語意來源為既有種子模板(PAYMENT_RECEIVED/STATEMENT_ISSUED),非臆造。- ★ 揭露架構性問題:3 條消費邊所描述的行為已由既有同步呼叫完成
(auth→limit 佔用/payment→posting 入帳/statement→posting 查詢)→ 並存即重複套用,
須先裁定「事件是唯一路徑,抑或同步為主、事件僅供投影/通知」。- 其餘分為
C-3(payload 缺口)/C-4(冗餘同步路徑)/C-5(語意未定義)/
C-6(無模板)/C-7(範圍大)/C-8(待生產端)/C-9(清單權威未定)。已完成(
#A-141):C-1/C-2——payment.payment.posted.v1→ notification(PAYMENT_RECEIVED)、
statement.statement.issued.v1→ notification(STATEMENT_ISSUED);真機驗證通過。
★ 前置契約變更:兩事件 payload 原缺currency而模板需要 → 依SAD-004 §5.3補為可選(不升版)。已完成(
#A-143):C-5之首項——party之卡片狀態投影(card.card.status-changed.v1→CC_PARTY.card_status_caches;
讀取端點GET /api/v1/accounts/{accountId}/cards)。★ 架構上之唯一解:card-service之 ACL 只允許cc-authorization-service
→party無法同步取得卡片狀態 → 事件投影是唯一途徑。真機驗證 8 項通過(含兩負樣本)。
詳CR-031(v1.1)/SCS-004 §7.5。⚠️C-5其餘條目仍待逐條立讀模型規格。裁定暫緩(
#A-143):C-7(參數快取)——實測 9 個可呼叫product之服務全部沒有參數快取,
且參數化自載「已登記為後續項」→ 無快取可失效 = 建監聽器即假綠。設計已立(CR-031 §3),
參數化一落地即 drop-in。★ 併更正SAD-004 §5.2:該事件之消費者「全體」實為「可呼叫product之 9 服務」(ACL 所限)。原載之範圍(保留供對照):
posting.transaction.posted.v1(statement/rewards/limit 之跨服務投影)、
statement.statement.issued.v1(payment)、installment.plan.created.v1(limit)、
clearing.batch.matched.v1、dispute.case.opened.v1、rewards.points.*、product.parameter.changed.v1。
批次 D|暫緩
notification.sent.v1(發送通道為真阻塞,GOV-006 §3.D);report.job.completed.v1/migration.job.completed.v1(無消費者)。
7. 明確不建議做的事(避免假綠)
-
不要為了「打通」而新建沒有語意來源的表——例如為
dispute.case.opened.v1在 posting 造一張表。先有規格、才有表(GOV-003條件 9)。 -
不要實作
SAD-003 §4.1的q_*自投影(除非 #6 裁定為跨服務投影)——會與權威表並存成兩套真相。 - 不要一次補完 13 個生產端再回頭做消費端——那會產出 13 個「有人發、沒人收」的事件(Kafka 主題堆積、無人驗證)。建議生產+消費成對交付(如批次 A-1)。
-
不要動
REFERRAL的行為(§5 #8)——待裁定。
8. 請求裁定事項(給 BEAM PMO)
- §5 #1~#4(hold 事件群):請擇一;建議 #1(a)+#2(a)+#3(併)+#4(b)。
- §5 #5(事件清單權威):建議 (c) 合併為單一清單。
-
§5 #6(
q_*語意):建議改寫為跨服務投影,否則廢除。 -
§5 #7(
declined.v1):建議 (b) 不納入(除非客戶要求拒絕率即時看板)。 -
§5 #8(
REFERRAL):建議 (b)。 -
是否同意「先做批次 A-1(
payment.payment.posted.v1生產+消費)」——該條不依賴任何裁定。 -
§5 #15(Kafka 主題命名不一致):建議 (a) 對齊文件慣例——4 服務各改 1 個常數
(product/clearing/dispute/posting),主題由「多一層重複」改為文件所示之形狀;
現階段零消費者,成本極低。⚠️ 本批已依 (a) 實作 4 個新服務,故新舊目前不一致。 -
★ §5 #16(事件 vs 同步呼叫之重複)—— 請【優先】裁定:建議 (b)「同步為主、事件為輔」——事件僅供投影/通知,不重做同步已做之寫入;
SAD-004 §5.2之「用途」欄須改寫以排除「重做寫入」之語意。理由:① 授權/繳款之即時性使同步不可去;② 事件驅動之價值在跨服務投影與通知,重做寫入無增益且引入重複套用風險;③ (b) 下批次 C 始可大規模推進(C-1/C-2即採此)。詳CR-029 §6。 -
§5 #5(清單權威)與 #6(
q_*語意)—— 請一併裁定:二者直接決定批次 C 之範圍(#5決定「哪些消費邊存在」;#6決定「7 個自投影是否要做」)。詳CR-029 §3/§8.5。 -
§5 #10 之追認:
CR-029已依建議 (b) 為payment.posted.v1及statement.issued.v1補可選currency(不升版),請追認或指示改為必填/升版。 -
CR-029 §5 C-6(無模板之通知):rewards.points.*/dispute.case.opened之通知是否要做?若要做,模板(業務文案)由誰提供?(不應由開發自行編寫)
9. 修訂記錄
| 版本 | 說明 |
|---|---|
| 1.0 | 初版:W-2 事件流推廣盤點(生產端 13 服務就緒度、消費端就緒度、8 項文件矛盾、四批次排序建議) |
| 1.1 | 依 BEAM PMO 同意執行 批次 A-1(payment.payment.posted.v1 生產+消費,#A-124)——①§1 現況更新(6 條已處理:3 全鏈路 + 3 僅生產端);②§6 批次 A-1 標記完成(真機:used_amount 500,000 → 300,000;冪等/亂序/溢繳下限皆通過);③實作中再發現 2 項契約缺口 → 新增 §5 #9(UNMATCHED/REVERSED 事件未實作,額度不會被重新佔用)、§5 #10(posted.v1 缺 currency,消費端無法精確定位額度主檔);④狀態改為「批次 A-1 完成;10 項待裁定」 |
| 1.2 | 依 BEAM PMO 指示續做 A-2/A-3(#A-125/#A-126)——①§1 現況更新(8 條已處理:4 全鏈路/含一消費端 + 4 僅生產端);②§6 批次 A 全數標記完成(A-2 真機:授權核准 → 通知建立、AUTH_APPROVED、4 通道、冪等/亂序通過;A-3 真機:發卡/綁 Token → 兩主題自動建立、payload 逐欄一致);③再發現 2 項 → §5 #11(approved.v1 主動擴充 2 個可選欄位 cardNoMasked/merchantId;舊事件將因變數不足而略過)、§5 #12(card.issued/token.bound 之 sequenceNo 與 status-changed 不同源,同聚合可能交錯);④狀態改為「批次 A 完成;12 項待裁定」 |
| 1.3 | 依 BEAM PMO 指示「hold 事件群四項矛盾請一一化解」→ 立 CR-028(先取三項客觀證據,再逐項定案)——①**§5 #1~#4 標記為「已化解」(無需 BEAM PMO 裁定):廢除 authorization.hold.*、auth 訂閱 limit.hold.* 兩者、limit.hold.created.v1 補 authorizationId(可選);②§6 批次 B 之規格障礙標記為「已解除」(藍圖見 CR-028 §6);③新增 §5 #13**——SDD-006 §4.2 之 card.card.status-changed.v1 payload 與實作不符(已依實作更正,列待覆核);④標題由「八項」更正為「十二項」(原標題漏隨 #9~#12 更新);⑤狀態改為「#5~#13(九項)待裁定」 |
| 1.4 | 依 BEAM PMO 指示「批次 B 仍需嚴謹的執行,請動工吧」(#A-128)——①§1 現況更新(8 條已處理:5 全鏈路 + 3 僅生產端);②§6 批次 B 標記完成;③新增 §5 #14——limit.hold.created.v1 payload 缺 currency(CC_AUTH.LIMIT_HOLDS.currency 為 NOT NULL;已補為可選,與 #10 同型);④★ 併載「動工前查證攔下之關鍵風險」:stand-in 後送會建立權威 hold → 若以 holdId 盲目插入將重複扣減可用額度 → 定案以 authorizationId 去重(真機以負樣本驗證);⑤狀態改為「批次 A/B 完成;#5~#14(十項)待裁定」 |
| 1.5 | 依 BEAM PMO 指示「繼續批次 A-4,從 payload 已定義的服務下手(posting/statement/rewards/clearing/dispute/merchant/notification),每個都要比照本次:規格先行 → 實作 → 真機驗證」(#A-133~#A-138)——①**§6 批次 A-4 表全面更新**:payload 已定義之 9 項事件全數完成(posting×2/statement/rewards×2/clearing/dispute/merchant/notification);併刪除原重複之第 14 列(dispute 原誤列兩次);②新增 §5 #15——★ Kafka 主題命名不一致(4 服務寫入「完整名」致主題多一層重複,與 SAD-004 §5.4 示例及 SCS-002/SCS-003 既有記載不符);本批新服務已依文件慣例實作 → 新舊不一致,待裁定對齊方向;③★ 附註更正 #A-131 之根因——原記「受服務間 ACL 阻擋」,實為本機 SSO 開啟(CARDCORE_SECURITY_ENABLED=true)→ 401 CC-PLT-4010(非 ACL 之 403);以合法使用者權杖重跑即通,clearing 已補驗完成;④**§8 新增第 7 項請求裁定**(#15);⑤實作中由單測攔下 1 項語意缺陷(dispute 之 disputeType 誤取程序類型軸);⑥狀態改為「批次 A/B/A-4(payload 已定義者)完成;#5~#15(十一項)待裁定」 |
| 1.6 | 依 BEAM PMO 指示「繼續批次 C(消費端),一樣擬定計劃、規格先行、任務追蹤清單缺一不可」(#A-141)——①新增 §6 批次 C 之計劃摘要並指向新建之 CR-029(消費端盤點與讀模型設計建議);②**§5 新增 #16**——★ 事件 vs 同步呼叫之重複(3 條消費邊所描述的行為已由既有同步呼叫完成,並存即重複套用);③**§5 #10 標記「已依 (b) 執行」(payment.posted.v1 + statement.issued.v1 補可選** currency)並請求追認;④**§8 新增第 8~11 項請求裁定**(#16 優先/#5+#6/#10 追認/無模板之通知);⑤狀態改為「批次 C 🟡(計劃已立;C-1/C-2 完成);#5~#16(十二項)待裁定」 |
| 1.7 | 依 BEAM PMO 指示「以 Best Practice 直接將這些問題解決」(#A-142)——★ §5 之 12 項全數裁定,改由架構師依業界實務決定(不再逐項回問):立 CR-030(含 4 條通則 P1~P4 作為方法論)。★ 其中 2 項為實證之真缺陷:#8(REFERRAL 已佔額度卻不發射事件——AuthorizeUseCase 之 willApprove 證實佔用、發射條件卻以 isApproved() 排除)、#9(UNMATCHED/沖正撤銷額度恢復卻未告知 limit → 可用額度永久高估)。★ #16 連帶收斂 CR-029 批次 C 之範圍(4 條改判「不做」)。★ #5 併補登 card.issued/token.bound/merchant.settlement.submitted/notification.sent 至權威清單。§5 上方新增裁定總表(12 項 × 裁定 × 執行狀態);另列 4 項留待外部輸入(業務文案/卡組織規範/聯絡資訊/累點規則) |
| 1.8 | 依 BEAM PMO 指示「批次 C 剩下的(party 卡片狀態投影、各服務參數快取)都需先立讀模型規格,我可以直接依 CR-030 的通則自行設計並推進」(#A-143)——①**§6 批次 C 標記 C-5 首項完成**:party 之卡片狀態投影(card.card.status-changed.v1 → CC_PARTY.card_status_caches;端點 GET /api/v1/accounts/{accountId}/cards)——★ 架構上之唯一解(card-service 之 ACL 只允許 cc-authorization-service → party 無法同步取得卡片狀態);真機驗證 8 項通過(含兩負樣本:重複 eventId/較舊 sequenceNo)。②**C-7(參數快取)標記「裁定暫緩」**——實測 9 個可呼叫 product 之服務全部沒有參數快取,且參數化自載「已登記為後續項」→ 無快取可失效 = 建監聽器即假綠;設計已立(CR-031 §3),參數化落地即 drop-in。詳 CR-031(v1.1)。③★ 併更正 SAD-004 §5.2:product.parameter.changed.v1 之消費者原載「全體」,惟 ACL 只允許 9 個服務呼叫 product → 更正為「可呼叫 product 之 9 服務」 |