跳到內容

領域事件

領域事件是解耦機制,讓各功能不必直接相依就能溝通。功能 A 不再呼叫功能 B,而是由功能 A 發佈事件、功能 B 訂閱它。

DomainEventBus 是一條行程內的廣播式發佈/訂閱匯流排。發佈方呼叫 publish(event);訂閱者消費帶型別的 on<T>() 串流。每個事件都實作 DomainEvent,並攜帶 occurredAt。

這條匯流排執行於 cc_server 內部。 每個發佈方與每個存活的訂閱者都在其中。沒有任何用戶端看得見這條匯流排。用戶端看到的是伺服器重新以 notifications/* JSON-RPC 訊框發出的精選子集。

沒有事件,每一次跨功能的反應都是一次匯入。通知路徑為了知道何時該彈出提示,就得匯入代理功能、pull request 功能、工單與會議——而那些功能到頭來又都得知道通知的存在。工單還得匯入管線,好讓完成的工單能推進一次執行。相依圖就此纏成死結,共享核心也不再共享。

發佈事件打破了這一點。通知路徑訂閱二十一個事件類別,卻不匯入任何引發它們的功能。管線觸發器派送器訂閱整條匯流排,對 pull request、工單或會議一無所知。可觀測性匯總執行結果,而不伸手進派送。

代價是常見的那一種:發佈方無法得知有沒有人在監聽,而一個從未被建構的監聽器會無聲地處於惰性狀態。TicketRemoteSyncHandler 就是長期存在的例子:它已被宣告、有測試涵蓋,卻從未接上正式環境。

有一件事匯流排_並不_負責,值得把話說清楚。

稽核軌跡不是事件串流的投影。 每一個變更型 RPC 操作預設都會受到稽核——RepoOp.audited 預設為 true,操作唯有宣告 audited: false 才能退出,而這是寫在操作本身、清楚可見的刻意之舉。派送器會在呼叫成功後,直接追加那一列「誰在何地做了什麼」。並不存在 DomainEventAuditBridge。ActivityLogPersister 會從 ActivityLogged 事件寫入 activity_log 資料列;那是與 RPC 稽核各自獨立的一條資料流。

事件匯流排_確實_是通知與自動化的傳輸通道。它不是負責記帳的權威記錄系統。

  • 通知。 伺服器把二十一個事件類別轉成 notifications/* 訊框,記錄到每個工作區各自持久保存的推送串流,並廣播給已連線的工作階段。若某個訊框的 workspace_id 指向的,是工作階段使用者並非其成員的工作區,該訊框會在伺服器上被丟棄;用戶端接著會把其餘訊框篩選到它正在檢視的那個工作區。
  • 事件驅動的自動化。 管線觸發器訂閱匯流排,並自動啟動相符的管線(見下文)。
  • 生命週期反應。 建立工作區會初始化它的 CEO 與專家代理、內建管線範本,以及入門評測套件。代理執行完成後會恢復暫停的管線步驟、關閉任務、向目標監督者提供輸入、可喚醒指定的檢查者,並可重新觸發團隊領導者。已合併的 PR 或被刪除的空間會觸發工作樹垃圾回收。
  • 跨供應商同步。 監聽五種工單事件的協調器,會把本機工單變更推送給已設定的供應商。

事件涵蓋整個產品面。概括來說:

  • 工作區、代理與儲存庫 —— 工作區建立、執行完成、儲存庫註冊、技能更新
  • Pull request —— 發佈、狀態變更、合併、審查要求、提及、自己撰寫的 PR 監看、外部偵測到的 PR
  • 訊息 —— 訊息、空間與空間佈建進度
  • 工單 —— 建立、指派、改派、詳細資料、狀態與三種終態結果
  • 任務 —— 針對單次派送執行的有序生命週期訊框,讓用戶端能排序並去除重複
  • 管線 —— 僅有執行的終態事件(已完成、已取消、已失敗)
  • 可觀測性 —— 活動日誌項目與預算閾值
  • 身分與成員資格 —— 成員新增、移除、角色變更
  • 日曆與會議 —— 驗證到期、會議開始與錄製結束
  • 機台 —— 接管、回收與意外關閉

記憶、編排、計畫文件、成品、核准、工單同步接收、日曆重新整理、工作樹合併、使用者建立、邀請與裝置撤銷都不是領域事件;請參閱參考頁。

完整的目錄——全部 53 個具體事件類別、它們的承載內容,以及消費它們的 24 個訂閱者——請參閱領域事件參考。那一頁是清單,這一頁是模型。

成員與裝置事件看起來對稱,其實不然——這一點在你推敲「撤銷多快生效」時很關鍵。

撤銷裝置確實會終止它的工作階段。 伺服器會監看已配對的裝置表,只要某台裝置離開使用中集合,與它對應的任何存活工作階段都會在數秒內被丟棄。

移除成員並不會關閉通訊端。 WorkspaceMemberRemoved 會被消費:LocalRpcServer 與 RemoteRelayHost 會移除該使用者已掛上的工作區訂閱,RemoteEventForwarder 則會使其成員資格快取失效,因此不會再推送該工作區的後續訊框。真正拒絕下一次 RPC 的仍然是角色閘門,它會在每一次呼叫時重新解析成員資格——所以他們的下一個請求會被拒絕,並附上「Not a member of this workspace」。對他們嘗試做的任何事而言,效果都是即時的;只是這並非靠掛斷他們來達成。

管線觸發器訂閱領域事件,並自動啟動相符的管線。PipelineTriggerDispatcher 會監聽整條匯流排,然後:

  1. 若事件型別不是它所知能對應成承載內容的型別,就直接短路返回
  2. 在每個工作區中查找與該事件型別相符的已啟用觸發器
  3. 將每個觸發器篩選到它所屬的工作區,並套用其承載內容比對篩選器
  4. 為每個通過篩選的觸發器啟動一次執行

關鍵的約束在第 1 步。匯流排承載 53 種事件型別;新增觸發器選擇器只會為以下十四種啟動執行(EventPayloadMapper 的承載內容分支): ExternalPrDetected、PullRequestPublished、PullRequestStatusChanged、 PrMerged、MessageReceived、TicketAssigned、 TicketCompleted、TicketFailed、TicketCancelled、 BudgetThresholdCrossed、RepoAdded、MeetingRecordingStopped、SkillUpdated 和 SpaceDeleted。除此之外,沒有任何東西能啟動管線。

RemoteEventForwarder 會把通知類事件轉譯成傳輸訊框,並透過 RPC 推送到已連線的工作階段,在那裡由訊框對應器將它們轉成應用程式內通知。因此,精簡用戶端看到的是伺服器事件串流的一份投影,本身不承擔任何執行——而這正是精簡用戶端拆分的重點所在。

不過,並非每個推送出去的訊框都是通知。任務生命週期串流與工單改派會即時轉送以驅動介面,但它們不會被記錄到通知串流中,也不會彈出任何提示。