The Standard Events について
The Standard Eventsは、The Standardに沿ったイベント駆動通信の実装とレビューを支援するスキルです。イベントサービスとブローカーの構成、検証・例外処理、オーケストレーション、起動時の登録、テスト規約を定めています。
主な機能
- 01CulDeSacイベントパターンの指針
- 02イベントブローカーとサービスの構成
- 03公開・購読操作の契約
- 04検証と例外マッピング規則
- 05起動時の有効化と依存性注入規則
- 06操作別のテスト規約
活用シーン
- 01サービス間の一方向ドメインイベント設計
- 02イベントサービスの検証とエラー処理のレビュー
- 03アプリケーション起動時の購読登録
- 04公開・購読操作のテスト構成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ hassanhabib/ the-standard-skills --skill the-standard-eventsインストール後、Claude Code で/the-standard-eventsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
manifest.jsoncontracts/contracts.jsonexamples/bad/example_bad_event_service.csexamples/good/example_event_service_orchestration_di.csexample_event_service_publish.csexample_event_service_subscribe.csrules/rules.jsonrules.mdtemplates/brokers/event_broker_template.cstemplates/foundations/event_service_template.csevent_service_test_subscribe_template.csevent_service_test_class_subscribe_template.csevent_service_test_class_root_template.csevent_service_test_publish_template.csevent_service_test_class_publish_template.csvalidations/checklist.mdanti-patterns.md詳細
標準イベント
このスキルについて
このスキルでは、The Standard に準拠したイベントの設計、実装、テストの方法を定めます。 CulDeSac パターン、イベントブローカー、基盤イベントサービス、検証、例外のマッピング、Publish 操作と Subscribe 操作のテスト構成を扱います。
明示的な対象範囲
このスキルでは、以下を明示的に扱います。
- CulDeSac パターンと、Standard 準拠システムでこのパターンを採用する理由
- イベントブローカーの構造と実装
- 基盤イベントサービスの実装:main、Validations、Exceptions ファイル
- Publish 操作と SubscribeTo 操作の契約および命名規則
- null エンティティと null イベントハンドラーの検証ルール
- 例外のマッピング:Validation 例外と Service 例外のみ(Dependency 例外は対象外)
- ブローカー、サービス、テストのファイル構成と partial クラスの構成
- テスト構成:操作ごとの Logic、Validations、Exceptions
- イベントレイヤー全体で適用される命名規則
- オーケストレーションとの統合:発行側と購読側のオーケストレーションサービスがイベントサービスをどのように利用するか
- 依存性注入の登録:イベントブローカーとイベントサービスに必要なライフタイム
- 起動時の有効化:SubscribeTo 呼び出しをアプリケーションの起動時にどこでどのように実行するか
使用するタイミング
サービス間のイベント駆動型通信を実装、レビュー、拡張、またはテストする際は、いつでもこのスキルを使用してください。 ドメインイベントの発行方法、イベントの購読方法、イベントサービスでの検証方法や失敗の処理方法を判断する際にも使用してください。 特定のサービスに CulDeSac パターンが適しているかを判断するときにも使用してください。
CulDeSac パターン
CulDeSac パターンは、サービスがドメインイベントを外部に送信し、どの購読者からも同期的な応答を期待しない場合に適用します。 これは呼び出しグラフの行き止まりです。データは一方向に流れ、呼び出し元は結果を待ってブロックされず、購読者は独立して応答できます。
イベントを使用する理由
- 境界づけられたコンテキスト間のサービスを疎結合にし、サービス間の直接的な依存関係を作らないため。
- 単一のドメインイベントに対して、複数の購読者が独立して応答できるようにするため。
- すべての下流の利用者を把握する必要があるオーケストレーションサービスを作らずに済むため。
- 発行側が購読者からの確認を必要としない、ファイア・アンド・フォーゲット型のフローをサポートするため。
- 発行側に影響を与えることなく、下流サービスをスケーリングし、進化させられるようにするため。
- 時間の経過とともに、単一のオーケストレーターに責任が過度に集中するのを防ぐため。
イベントサービスの原則
- イベントサービスは常に基盤サービスです。
- イベントサービスは、ドメインモデルとイベントインフラストラクチャの境界に位置します。
- イベントサービスが依存できるのはイベントブローカーのみであり、他のサービスに依存してはなりません。
- イベントサービスが公開する操作は、エンティティごとに Publish[Entity]Async と SubscribeTo[Entity]Event の2つだけです。
- イベントサービスにはログブローカーを注入しません。設計上、ログへの依存関係はありません。
- イベントサービスでは、例外処理をビジネスロジックから分離するために、2つの TryCatch デリゲートを使用します。
- イベントサービスは Dependency 例外や CriticalDependency 例外を捕捉しません。HTTP API やストレージ API を呼び出さないためです。
ブローカー実装の原則
- イベントブローカーは、イベントサービスが依存する唯一のインフラストラクチャです。
- イベントブローカーは任意のイベントインフラストラクチャをラップします。特定のイベントライブラリを指定するものではありません。
- 各エンティティには、そのエンティティ型に特化したイベントクライアントのインスタンスを1つ用意し、ブローカーのコンストラクターで宣言します。
- ブローカーは4つのファイルに分割します。ベースインターフェイス、エンティティインターフェイスの partial、ベース実装、エンティティ実装の partial です。
- ブローカーの操作は、基盤となるイベントインフラストラクチャへの薄いパススルーとします。ブローカー内にロジックは置きません。
オーケストレーションとの統合
イベントサービスを利用するのは、オーケストレーションサービスのみです。 コントローラー、処理サービス、その他の基盤サービスは、イベントサービスに直接依存してはなりません。
パブリッシャー側
ドメインイベントを発生させる必要があるオーケストレーションサービスは、イベントサービスの Publish[Entity]Async を呼び出します。
オーケストレーションサービスがエンティティの構成を担い、イベントサービスにパブリッシュ呼び出しを委譲します。
パブリッシャーであるオーケストレーションサービスは、どのサービスがサブスクライブするかについては何も知りません。パブリッシュする必要があることだけを把握しています。
サブスクライバー側
ドメインイベントに反応する必要があるオーケストレーションサービスは、SubscribeTo[Entity]Events(複数形)という名前のメソッドを公開します。
このメソッドは、ハンドラーとしてプライベートな非同期メソッドを渡し、this.[entity]EventService.SubscribeTo[Entity]Event(handler) を呼び出します。
ハンドラーはエンティティを受け取り、必要に応じて基盤サービスを呼び出して、オーケストレーション上の反応を実行します。
イベントに対して何を行うかを把握しているのはサブスクライバーであるオーケストレーションサービスだけです。パブリッシャーはサブスクライバーを認識しません。
命名の区別:複数形と単数形
- イベントサービスのメソッドは常に単数形です:
SubscribeTo[Entity]Event。 - オーケストレーションサービスのラッパーメソッドは常に複数形です:
SubscribeTo[Entity]Events。 - オーケストレーションサービスがイベントを複数のサブ操作に振り分ける場合があるため、ラッパーは複数形です。
- 起動時に呼び出すのは複数形のラッパーです。単数形のイベントサービスメソッドを直接呼び出してはいけません。
依存性注入と起動時の有効化
登録ルール
- パブリッシュまたはサブスクライブを行うすべてのオーケストレーションサービスを DI に登録する必要があります。
IEventBrokerとEventBrokerに必要な DI ライフタイムは、使用するイベント基盤によって異なります。- インメモリーイベント基盤(LeVent など)の場合、正しく動作させるにはシングルトンが必要です。クライアントインスタンスはサブスクリプションハンドラーの登録をメモリー内に保持します。スコープまたはトランジェントで登録すると、登録済みハンドラーのない新しいインスタンスが作られるため、イベントは配信されません。
- 外部イベント基盤(Azure Service Bus や EventHighway など)の場合は、クライアントライブラリ独自のライフタイム推奨事項に従ってください。サブスクリプションは外部サービスに保存され、クライアントインスタンスとは独立して維持されますが、接続を再利用し、メッセージプロセッサーを稼働し続けるために、通常はクライアントをシングルトンにする必要があります。
I[Entity]EventServiceと[Entity]EventServiceは、依存先のブローカーに適合するライフタイムで登録する必要があります。サービスをブローカーより長いライフタイムで登録してはいけません。- 判断に迷う場合はシングルトンを優先してください。既知のすべてのイベント基盤で安全に使用でき、サブスクリプションが気付かれないまま失敗するのを防げます。
起動時の有効化ルール
- イベントサブスクリプションは自動的には有効になりません。アプリケーションの起動時に明示的に開始する必要があります。
- DI コンテナーの構築後、サブスクライブを行うすべてのオーケストレーションサービスで
SubscribeTo[Entity]Events()メソッドを呼び出す必要があります。 - 有効化の呼び出しは、
Configure()(Startup.cs スタイル)または最小限の API ホスト設定における同等の箇所に記述する必要があります。 - Startup.cs では、
app.ApplicationServices.GetService<I[Entity]OrchestrationService>().SubscribeTo[Entity]Events()を使用します。 - 最小限の API(Program.cs)では、
app = builder.Build()の後にapp.Services.GetService<I[Entity]OrchestrationService>().SubscribeTo[Entity]Events()を使用します。 - 起動時の有効化呼び出しがない場合、サブスクライバーはイベントを受信できません。サブスクリプションは登録されないまま、何も通知されません。
- サブスクライブを行う各オーケストレーションサービスでは、サブスクリプションごとに起動時の有効化呼び出しを正確に 1 回行う必要があります。
イベントサービスの検証ルール
パブリッシュの検証
- パブリッシュする前に、エンティティが null でないことを検証します。
- エンティティが null の場合は、ただちに
NullEventExceptionをスローします(サーキットブレーカー)。 - エンティティが null の場合、それ以上のエラーを収集せず、ただちに処理を中断します。
サブスクライブの検証
- サブスクライブする前に、イベントハンドラーのデリゲートが null でないことを検証します。
- ハンドラーが null の場合は、ただちに
NullEventHandlerExceptionをスローします(サーキットブレーカー)。 - ハンドラーが null の場合、それ以上のエラーを収集せず、ただちに処理を中断します。
例外処理ルール
- すべての検証失敗では、内部例外をラップした [Entity]EventValidationException を発生させる必要があります。
- 予期しない例外はすべて FailedEventServiceException でラップし、さらに [Entity]EventServiceException でラップする必要があります。
- FailedEventServiceException は、元の例外を innerException として、その Data コレクションとともに保持する必要があります。
- イベントサービスでは、DependencyValidationException または DependencyException のカテゴリを使用しません。
- 例外処理をコアロジックから分離するため、TryCatch デリゲートを使用する必要があります。
命名規則
- インターフェース:I[Entity]EventService。
- 実装:[Entity]EventService(internal partial class)。
- 発行操作:Publish[Entity]Async(ValueTask を返す非同期処理)。
- 購読操作:SubscribeTo[Entity]Event(void を返す同期処理)。
- Subscribe の名前に ListenTo[Entity]Event を使用してはなりません。
- ブローカーインターフェース:IEventBroker(共通)。エンティティ固有の partial は IEventBroker_[Entity].cs に記述します。
- 例外の命名:Null[Entity]EventException、Null[Entity]EventHandlerException、Failed[Entity]EventServiceException、[Entity]EventValidationException、[Entity]EventServiceException。
イベントサービスのテストルール
- まず SubscribeTo[Entity]Events の正常系をテストします。
- 次に Publish[Entity]Async の正常系をテストします。
- 3番目に検証失敗(null ハンドラー、null エンティティ)をテストします。
- 4番目にサービス例外(両方の操作における予期しないエラー)をテストします。
- イベントサービステストでは loggingBrokerMock を宣言してはなりません。ロギングブローカーは存在しません。
- 購読ハンドラーのモックには Mock<Func<[Entity], ValueTask>> を使用する必要があります。
- すべてのテストの最後に必ず eventBrokerMock.VerifyNoOtherCalls() を呼び出します。
- 期待される呼び出しには Times.Once を使用し、検証失敗によってスキップされる呼び出しには Times.Never を使用します。