先定義業務事實,再決定事件名稱
一個可靠的事件名稱,應描述某個領域中已經發生的業務事實,而不是要求其他系統執行動作。customer.account.created 表示客戶帳號已建立;create.customer.account 比較像命令。兩者的責任不同:事件可以被多個未知的消費者訂閱,命令則通常有明確接收者。把命令偽裝成事件,日後很容易出現某個消費者失敗後,所有團隊都不知道誰應該重試或補償的情況。
實務上可採用 domain.entity.past-action 的結構,例如 sales.order.confirmed、inventory.stock.reserved、support.ticket.closed。domain 應代表穩定的業務邊界,而不是目前的部門名稱;entity 使用團隊共同理解的業務名詞;動作則使用完成式,清楚表達狀態已改變。避免 crm.customer.updated、line.message.sent 這類把產品或通道寫進核心事件的名稱,除非產品或通道本身就是業務語意的一部分。
updated 與 changed 雖然方便,卻常把重要差異藏在 payload 裡。訂單地址修正、付款狀態改變與取消原因更新,可能需要完全不同的權限、時效與後續處理。若消費者必須解析多個欄位才能知道發生什麼事,通常代表事件名稱太寬。反過來也不必為每一個資料欄位建立事件;判斷標準是該變更是否具有獨立的業務意義,以及消費者是否會採取不同動作。
統一事件信封,讓業務內容保持獨立
跨部門事件最好分成穩定的事件信封與領域資料。信封處理追蹤、去重、路由與版本辨識,data 才承載訂單、客戶或設備等業務內容。這能避免每個團隊自行發明 traceId、timestamp 或 source,也讓共用的監控與重送工具不必理解所有領域模型。
- event_id:每次發布都唯一,供消費者實作冪等處理;重新投遞同一事件時不應產生新值。
- event_type:保存穩定的語意名稱,例如 sales.order.confirmed,不與訊息佇列的 topic 名稱綁死。
- schema_version:明確指出 payload 契約版本,不以部署日期、Git commit 或服務版本代替。
- occurred_at:記錄業務事實發生的時間;若需要,也可另記發布時間,兩者不可混用。
- producer 與 subject:標示事件來源及主要實體識別值,方便查詢、授權與事件回放。
- correlation_id 與 causation_id:分別串起同一流程及直接觸發來源,協助追查跨系統失敗。
欄位名稱一致不等於語意一致。時間要約定時區與格式,金額要帶幣別,數量要說明單位,識別值也要說清楚是內部主鍵、外部單號或可公開的 ID。事件最好包含消費者完成該反應所需的業務快照,但不要複製整張資料表。資料太少會迫使消費者同步回查來源系統;資料太多則會增加敏感資訊外洩、契約膨脹與版本演進的成本。
以相容性判斷版本,而不是以修改次數判斷
版本應保護消費者,而不是記錄開發歷史。新增一個可選欄位,且舊消費者會忽略未知欄位時,通常可以維持原版本。重新命名或刪除欄位、改變型別、單位或識別值語意、把選填改成必填,以及讓同一狀態代表不同業務含義,都是破壞性變更。即使 JSON 結構沒有改,只要既有消費者可能做出不同決策,就應視為契約變更。
列舉值新增是常被低估的風險。理論上它是加法變更,但許多消費者會使用完整 switch,遇到未知值便失敗。因此契約應要求消費者具備 unknown 或預設處理,同時在新增會觸發新流程的列舉值前完成影響盤點。相同原則也適用於允許 null、精度改變與字串格式收緊:不能只看 schema 驗證是否通過,還要看消費端的實際行為。
建議讓 event_type 保持穩定,並在信封中用 schema_version 表示契約的主要版本。只有在訊息基礎設施必須隔離不同權限、保留期限或吞吐特性時,才把版本放進 topic。版本化 topic 雖能降低路由衝突,卻會增加訂閱、權限、監控與回放的維運成本。也不要因為任一小欄位新增就升主要版本;版本過多會讓發布者長期維護多套格式,最後沒有人能確認哪一版仍在使用。
用可執行的治理流程完成跨團隊演進
事件目錄至少要記錄事件擁有者、業務定義、schema、範例、資料敏感度、相容性政策與已知消費者。擁有者應是能決定業務語意的領域團隊,而不是訊息平台維運者。平台團隊可以制定信封與傳輸標準,但不應替業務團隊解釋 order.confirmed 到底代表付款完成、人工核准,還是僅通過格式檢查。
準備破壞性版本時,先盤點消費者與事件回放需求,再選擇雙重發布、轉換器或協調升級。雙重發布容易理解,但會增加發布端邏輯,消費者也可能重複處理同一業務事實;集中轉換器可減少來源系統負擔,卻形成需要監控與維護的新元件。若舊事件需要從儲存區回放,新版消費者是否能讀取歷史格式,也必須在上線前決定,不能等事故發生才補轉換。
每次演進都應搭配 schema 相容性檢查、發布者契約測試、代表性消費者測試,以及針對未知版本與解析失敗的告警。汰換公告需要列出替代版本、受影響事件、遷移方式、負責窗口與停止發布條件;期限則依消費者部署節奏與營運風險協議決定。最重要的是讓舊版真的能被移除。無限期雙重發布只會把暫時相容措施變成永久架構債務;成熟的整合團隊會把命名、版本、觀測與汰換視為同一份契約的一部分。
