領域事件
甚麼是領域事件
標題為「甚麼是領域事件」的章節領域事件是一種解耦機制,讓各功能無需直接依賴就能互相溝通。功能 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 個訂閱方——請參閱 領域事件參考。那一頁是清單,這一頁是模型。
即時存取控制依賴兩套不同機制
標題為「即時存取控制依賴兩套不同機制」的章節成員關係與裝置事件看似對稱,實則不然;如果你在推敲撤銷要多快才生效,這一點就 很重要。
撤銷裝置確實會終止它的工作階段。 伺服器會監視已配對的裝置表,只要某台裝置 離開了使用中集合,其對應的使用中工作階段就會在數秒內被中斷。
移除成員並不會關閉 Socket。 WorkspaceMemberRemoved 會被消費:
LocalRpcServer 與 RemoteRelayHost 會移除該使用者已掛載的工作區訂閱,
RemoteEventForwarder 則會使其成員關係快取失效,因此不會再推送該工作區的後續
訊框。真正攔下下一次 RPC 的仍然是角色閘門,它會在每一次呼叫時重新解析成員
關係——因此他們的下一個請求會被拒絕,並顯示「Not a member of this workspace」。
對他們嘗試做的任何事而言,效果都是即時的;只是這並非靠掛斷連線達成。
管線觸發器
標題為「管線觸發器」的章節管線觸發器訂閱領域事件,並自動啟動相符的管線。PipelineTriggerDispatcher
會監聽整條匯流排,然後:
- 除非事件類型是它知道如何對應到載荷的類型,否則直接短路返回
- 在每一個工作區中,查找與該事件類型相符的已啟用觸發器
- 把每個觸發器篩選到其自身的工作區,並套用它的載荷相符篩選條件
- 為每一個通過篩選的觸發器啟動一次執行
關鍵限制在第 1 步。匯流排承載 53 種事件類型;新增觸發器選擇器只會為以下十四種
啟動執行(EventPayloadMapper 的載荷分支):
ExternalPrDetected, PullRequestPublished, PullRequestStatusChanged,
PrMerged, MessageReceived, TicketAssigned,
TicketCompleted, TicketFailed, TicketCancelled,
BudgetThresholdCrossed, RepoAdded, MeetingRecordingStopped, SkillUpdated
以及 SpaceDeleted。除此之外的任何事件都無法啟動管線。
用戶端如何看到這一切
標題為「用戶端如何看到這一切」的章節RemoteEventForwarder 會把通知類事件轉成傳輸訊框,並透過 RPC 推送到已連線的
工作階段,在那裡由訊框映射器將它們轉成應用程式內通知。因此,精簡用戶端看到的
是伺服器事件串流的一個投影,本身並不承擔任何執行——這正是
精簡用戶端拆分的重點所在。
不過,並非每個推送的訊框都是通知。任務生命週期串流與工單改派會即時轉發以驅動 UI,但它們不會寫入通知動態,也不會彈出任何提示。