コンテンツにスキップ

ドメインイベント

ドメインイベントは、機能同士が直接的な依存関係を持たずにやり取りできるようにする疎結合の仕組みです。機能 A が機能 B を呼び出す代わりに、機能 A がイベントを発行し、機能 B がそれをサブスクライブします。

DomainEventBus はインプロセスのブロードキャスト型パブリッシュ/サブスクライブバスです。パブリッシャーは publish(event) を呼び出し、サブスクライバーは型付きの on<T>() ストリームを消費します。すべてのイベントは DomainEvent を実装し、occurredAt を保持します。

バスは cc_server の内部で動作します。 すべてのパブリッシャーと、稼働中のすべてのサブスクライバーはそこに存在します。このバスを目にするクライアントはありません。クライアントが見るのは、サーバーが notifications/* JSON-RPC フレームとして再送信する、厳選されたサブセットです。

イベントがなければ、機能をまたぐあらゆる反応はインポートになります。通知の経路は、トーストをいつ表示すべきかを知るためだけに、エージェント機能、プルリクエスト機能、チケッティング、ミーティングをインポートしなければならず、さらにそれらはいずれも通知が存在することを知らなければならなくなります。チケッティングは、完了したチケットが実行を進められるように、パイプラインをインポートすることになるでしょう。依存グラフは結び目となって閉じ、共有カーネルは共有されなくなります。

イベントを発行すれば、それが崩れます。通知の経路は 21 個のイベントクラスをサブスクライブし、それらを発生させる機能を一切インポートしません。パイプライントリガーのディスパッチャーはバス全体をサブスクライブし、プルリクエスト、チケット、ミーティングについて何も知りません。オブザーバビリティは、ディスパッチの内部に踏み込むことなく実行結果を集計します。

コストはいつもどおりです。パブリッシャーは誰かが聞いているかどうかを知ることができず、決して生成されないリスナーは静かに無効のままです。TicketRemoteSyncHandler がその常設の例です。これは宣言され、テストでカバーされていながら、本番では決して接続されません。

バスが 担わない ものについて、正確に述べておく価値があります。

監査証跡はイベントストリームの投影ではありません。 変更を伴うすべての RPC 操作はデフォルトで監査されます。RepoOp.audited はデフォルトで true であり、操作がオプトアウトできるのは audited: false を宣言した場合だけです。これは操作そのものに表れる意図的な行為です。ディスパッチャーは、呼び出しが成功した直後に「誰が、どこから、何をしたか」の行を追記します。DomainEventAuditBridge は存在しません。ActivityLogPersister は ActivityLogged イベントから activity_log の行を書き込みます。これは RPC 監査とは別のフィードです。

イベントバスは通知と自動化のための輸送手段 そのもの です。記録を管理するシステムではありません。

  • 通知。 21 個のイベントクラスがサーバーによって notifications/* ワイヤフレームに変換され、ワークスペースごとの永続フィードに記録され、接続中のセッションにブロードキャストされます。workspace_id が、セッションユーザーの所属していないワークスペースを指すフレームは、サーバー上で破棄されます。その後クライアントが、残りを自分が表示しているワークスペースに絞り込みます。
  • イベント駆動の自動化。 パイプライントリガーがバスをサブスクライブし、一致するパイプラインを自動起動します(後述)。
  • ライフサイクルの反応。 ワークスペースを作成すると、その CEO とスペシャリストエージェント、組み込みのパイプラインテンプレート、スターターの評価スイートがシードされます。完了したエージェント実行は、中断されたパイプラインステップを再開し、タスクをクローズし、ゴールスーパーバイザーに情報を供給し、名前を指定したチェッカーを起こすことができ、チームリーダーを再トリガーできます。PR がマージされたりスペースが削除されたりすると、ワークツリーのガベージコレクションがトリガーされます。
  • ベンダー間同期。 ローカルのチケット変更は、5 つのチケットイベントをリッスンするコーディネーターによって、設定済みのベンダーへプッシュされます。

イベントはプロダクトの表面全体に広がっています。大まかには次のとおりです。

  • ワークスペース、エージェント、リポジトリ — ワークスペースの作成、実行の完了、リポジトリの登録、スキルの更新
  • プルリクエスト — 公開、ステータス変更、マージ、レビュー依頼、メンション、自作 PR のウォッチ、外部で検出された PR
  • メッセージング — メッセージ、スペース、スペースのプロビジョニング進捗
  • チケット — 作成、割り当て、再割り当て、詳細、ステータス、および 3 つの終端結果
  • タスク — 1 回のディスパッチされた実行に対する順序付きライフサイクルフレーム。クライアントが並べ替えと重複排除を行えるようにします
  • パイプライン — 終端の実行イベントのみ(完了、キャンセル、失敗)
  • オブザーバビリティ — アクティビティログのエントリと予算しきい値
  • アイデンティティとメンバーシップ — メンバーの追加、削除、ロール変更
  • カレンダーとミーティング — 認証の有効期限切れ、ミーティングの開始、録画の終了
  • リグ — テイクオーバー、回収、予期しないクローズ

メモリ、オーケストレーション、プランドキュメント、アーティファクト、承認、チケット同期の取り込み、カレンダーの更新、ワークツリーのマージ、ユーザーの作成、招待、デバイスの失効はドメインイベントではありません。リファレンスを参照してください。

完全なカタログ(53 個の具象イベントクラスすべて、それらのペイロード、およびそれらを消費する 24 個のサブスクライバー)については、ドメインイベントリファレンスを参照してください。あのページは一覧であり、このページはモデルです。

ライブアクセス制御は 2 つの異なる仕組みで成り立つ

「ライブアクセス制御は 2 つの異なる仕組みで成り立つ」というセクション

メンバーシップのイベントとデバイスのイベントは対称に見えますが、実際にはそうではありません。失効がどれだけ速く反映されるかを考えるなら、この違いは重要です。

デバイスを失効させると、そのセッションは確かに終了します。 サーバーはペアリングされたデバイスのテーブルを監視し、デバイスがアクティブな集合から外れた稼働中セッションを、数秒以内に切断します。

メンバーを削除してもソケットは閉じません。 WorkspaceMemberRemoved は消費されます。LocalRpcServer と RemoteRelayHost は、そのユーザーがアタッチしたワークスペース購読を削除し、RemoteEventForwarder はメンバーシップキャッシュを無効化するため、そのワークスペース向けの以降のフレームはプッシュされません。次の RPC を拒否するのは依然としてロールゲートであり、これは すべての 呼び出しでメンバーシップを再解決します。したがって、そのユーザーの次のリクエストは “Not a member of this workspace” で拒否されます。影響は、そのユーザーが行おうとするあらゆることに対して即座に現れます。ただし、それは接続を切ることによって達成されるわけではありません。

パイプライントリガーはドメインイベントをサブスクライブし、一致するパイプラインを自動起動します。PipelineTriggerDispatcher はバス全体をリッスンし、次のことを行います。

  1. イベントタイプが、ペイロードへのマッピング方法を把握しているものでない限り、早期に処理を打ち切る
  2. そのイベントタイプに一致する有効なトリガーを、すべてのワークスペースにわたって検索する
  3. それぞれを自身のワークスペースに絞り込み、ペイロード一致フィルターを適用する
  4. 絞り込みを通過した各トリガーに対して実行を開始する

重要な制約はステップ 1 です。バスは 53 種類のイベントタイプを運びますが、トリガー追加ピッカーが実行を開始するのは次の 14 種類です(EventPayloadMapper のペイロード分岐): ExternalPrDetected, PullRequestPublished, PullRequestStatusChanged, PrMerged, MessageReceived, TicketAssigned, TicketCompleted, TicketFailed, TicketCancelled, BudgetThresholdCrossed, RepoAdded, MeetingRecordingStopped, SkillUpdated および SpaceDeleted。それ以外ではパイプラインを開始できません。

RemoteEventForwarder は通知クラスのイベントをワイヤフレームに変換し、RPC 経由で接続中のセッションにプッシュします。そこでフレームマッパーがそれらをアプリ内通知に変換します。したがってシンクライアントは、実行を一切所有することなく、サーバーのイベントストリームの投影を目にすることになります。これがシンクライアント分割の狙いそのものです。

ただし、プッシュされるすべてのフレームが通知というわけではありません。タスクライフサイクルのストリームとチケットの再割り当ては、UI を駆動するためにライブで転送されますが、通知フィードには記録されず、トーストも表示されません。