Salesforce Platform Events を活用した堅牢なイベント駆動型アーキテクチャの構築

背景と適用シナリオ

Salesforce 建築家 (Salesforce Architect) の視点から、今日のエンタープライズシステムにおける最大の課題の一つは、サイロ化されたシステム間のシームレスな連携です。従来のポイント・ツー・ポイントの連携は、システム数が増えるにつれて複雑性が指数関数的に増大し、「スパゲッティ・アーキテクチャ」と呼ばれる保守困難な状態に陥りがちです。この課題を解決する強力なパラダイムが Event-Driven Architecture (EDA)、日本語で言う「イベント駆動型アーキテクチャ」です。

EDA は、システムのコンポーネントが「イベント」を介して非同期に通信する設計モデルです。あるシステムで状態変化(イベント)が発生すると、そのイベントがブロードキャストされ、関心のある他のシステムがそのイベントを購読して自身の処理を実行します。このモデルは、システム間の疎結合を実現し、スケーラビリティ、回復力、俊敏性を大幅に向上させます。

Salesforce が提供する Platform Events (プラットフォームイベント) は、この EDA を Salesforce プラットフォーム上でネイティブに実現するためのメッセージング基盤です。これにより、Salesforce 内部のプロセス間、あるいは Salesforce と外部システム間でのリアルタイムな非同期通信が可能になります。

適用シナリオの例:

  • 注文管理システム連携: 外部の E コマースサイトで新規注文が入ると、注文情報をペイロードに含んだ Platform Event を発行します。Salesforce 側では、そのイベントを購読するプロセス(Apex トリガやフロー)が起動し、自動的に商談や注文オブジェクトのレコードを作成します。
  • IoT デバイス連携: 工場に設置されたセンサーが異常な温度を検知した際に Platform Event を発行します。Salesforce はこれを購読し、サービスケースを起票してフィールドサービス担当者を自動で派遣するといった、プロアクティブな顧客対応を実現します。
  • 複数システムのデータ同期: ある基幹システムで顧客情報が更新された場合、その変更を Platform Event として Salesforce に通知します。Salesforce はイベントを受け取り、取引先責任者レコードを更新します。逆もまた然りで、Salesforce での変更を外部システムへ通知することも可能です。

これらのシナリオに共通するのは、発行者 (Publisher) と登録者 (Subscriber) が互いを直接意識する必要がないという点です。発行者はただイベントを叫ぶだけでよく、誰が聞いているかを知る必要はありません。この「疎結合」こそが、アーキテクチャに柔軟性と拡張性をもたらす鍵となります。


原理説明

Platform Events の仕組みを理解するために、いくつかの重要なコンポーネントを解説します。

1. イベントの定義 (Event Definition)

Platform Event は、カスタムオブジェクトを定義するのと同様に、[設定] メニューから宣言的に定義します。API 参照名は __e で終わります。カスタム項目を定義することで、イベントメッセージに含めるデータ構造(スキーマ)を決定します。例えば、「注文作成イベント」であれば、注文 ID、顧客 ID、商品コード、金額などの項目を定義します。

2. イベントバス (Event Bus)

イベントバスは、発行された全てのイベントメッセージを一時的に保持し、登録者へ配信するマルチテナント対応のメッセージブローカーです。Salesforce プラットフォームがこのイベントバスを管理しているため、我々はインフラについて心配する必要がありません。

3. 発行者 (Publisher)

発行者は、イベントメッセージを作成し、イベントバスに送信する役割を担います。Salesforce プラットフォームでは、以下の方法でイベントを発行できます。

  • Apex: EventBus.publish() メソッドを使用します。
  • フロー (Flow): 「レコードを作成」要素でプラットフォームイベントオブジェクトを選択します。
  • プロセスビルダー (Process Builder): ※新規利用は非推奨。フローへの移行が推奨されます。
  • API: REST API や SOAP API を介して、外部システムからイベントを発行します。

重要な点として、イベントの発行はトランザクションの最後にコミットされます。つまり、Apex トリガ内でイベントを発行した場合、そのトランザクションが全て正常に完了して初めて、イベントがイベントバスに送信されます。もしトランザクションがロールバックされれば、イベントの発行もキャンセルされます。

4. 登録者 (Subscriber)

登録者は、特定のイベントを待ち受け、イベントメッセージを受信した際に特定のアクションを実行します。登録方法は多岐にわたります。

  • Apex トリガ: Platform Event オブジェクトに対して after insert トリガを作成します。各イベントは独立したトランザクションで処理されます。
  • フロー (Flow): 「プラットフォームイベントによってトリガされるフロー」を作成します。
  • CometD クライアント: 外部アプリケーション(Web アプリケーションなど)が CometD というプロトコルを使ってイベントをリアルタイムに購読します。
  • MuleSoft やその他ミドルウェア: Salesforce Connector を使用してイベントを購読し、他のシステムへ連携する処理を構築します。

5. ReplayID とイベントの永続性

各イベントメッセージには、ReplayID (リプレイID) という一意の識別子が割り当てられます。これはイベントバス内でのイベントの位置を示すもので、単調増加する値です。登録者は、最後に処理したイベントの ReplayID を保持しておくことで、接続が切断された場合でも、切断された時点以降のイベントを再購読(リプレイ)できます。標準の Platform Event は 24 時間、イベントバスに保持されます。


サンプルコード

ここでは、Apex を使用して Platform Event を発行し、それを Apex トリガで購読する最も一般的な例を示します。まず、以下のような Platform Event「Cloud News」を定義したと仮定します。

  • API Name: Cloud_News__e
  • Fields:
    • Location__c (Text)
    • Urgency__c (Text)
    • News_Content__c (Long Text Area)

Apex によるイベントの発行

このコードは、いくつかのニュースイベントを作成し、EventBus.publish() メソッドで一括して発行します。リストとして渡すことで、単一の DML コールで複数のイベントを効率的に発行できます。

// イベントのリストを作成
List<Cloud_News__e> newsEvents = new List<Cloud_News__e>();

// 1つ目のイベントインスタンスを作成し、項目値を設定
newsEvents.add(new Cloud_News__e(
    Location__c='West',
    Urgency__c='High',
    News_Content__c='緊急メンテナンスのお知らせです。'
));

// 2つ目のイベントインスタンスを作成
newsEvents.add(new Cloud_News__e(
    Location__c='East',
    Urgency__c='Low',
    News_Content__c='新機能リリースのご案内。'
));

// イベントのリストを発行
List<Database.SaveResult> results = EventBus.publish(newsEvents);

// 結果を反復処理して、成功したかどうかを確認
for (Database.SaveResult sr : results) {
    if (sr.isSuccess()) {
        System.debug('Successfully published event with ID: ' + sr.getId());
    } else {
        for(Database.Error err : sr.getErrors()) {
            System.debug('Error returned: ' +
                        err.getStatusCode() +
                        ' - ' +
                        err.getMessage());
        }
    }
}

Apex トリガによるイベントの購読

このトリガは Cloud_News__e イベントが発行されるたびに起動します。after insert コンテキストでのみ動作します。トリガは、受け取ったイベントの内容に基づいてケースを作成します。

// Cloud_News__e イベントに対する after insert トリガ
trigger CloudNewsTrigger on Cloud_News__e (after insert) {

    // 作成するケースのリストを初期化
    List<Case> cases = new List<Case>();

    // Trigger.New には、受信したイベントメッセージのリストが含まれる
    // for ループで各イベントを処理
    for (Cloud_News__e event : Trigger.New) {
        System.debug('Received event: ' + event);

        // 緊急度が高い (High) イベントのみを対象とする
        if (event.Urgency__c == 'High') {
            // 新しいケースを作成
            Case cs = new Case();
            cs.Priority = 'High';
            cs.Subject = '緊急対応依頼: ' + event.Location__c + ' 地区';
            cs.Description = event.News_Content__c;
            cases.add(cs);
        }
    }

    // ケースのリストが空でない場合、一括で挿入
    if (!cases.isEmpty()) {
        insert cases;
    }
}

注意事項

Platform Events をアーキテクチャに組み込む際には、以下の点を考慮する必要があります。

権限

Platform Event オブジェクトに対する「作成」権限がなければイベントを発行できず、「参照」権限がなければ購読(Apex トリガ経由など)ができません。プロファイルや権限セットで適切に設定する必要があります。

API 制限 (Governor Limits)

Platform Events には、発行と配信に関するガバナ制限が存在します。

  • 発行の制限: 24 時間以内に発行できるイベント数には上限があります。この上限は Salesforce のエディションや、アドオンの購入によって異なります。Standard Volume Events よりも High-Volume Platform Events の方が、はるかに多くのイベントを発行できます。アーキテクチャ設計の初期段階で、予想されるイベント量を見積もり、適切なイベントタイプを選択することが極めて重要です。
  • 配信の制限: CometD クライアントなどの外部登録者へのイベント配信数にも上限があります。Apex トリガやフローによる内部購読には、この配信制限は適用されません。

制限を超過すると、イベントの発行や配信がブロックされるため、イベントモニタリングを定期的に行い、使用状況を監視することが推奨されます。

エラー処理

発行側のエラー: EventBus.publish() は、発行が成功したかどうかの結果を返します。前述のサンプルコードのように、必ず結果を確認し、失敗した場合はログ記録やリトライ処理を実装するべきです。

購読側のエラー: Apex トリガ内で例外が発生した場合、そのイベントの処理は失敗しますが、他のイベントの処理には影響を与えません。なぜなら、各イベントの配信は個別のトランザクションで実行されるからです。しかし、失敗したイベントは自動的には再試行されません。そのため、購読側のロジックは堅牢にし、予期せぬエラーが発生しても対応できるように、独自の再試行メカニズムやエラー通知の仕組みを検討する必要があります。例えば、プラットフォームキャッシュやカスタムオブジェクトを使用して、処理に失敗したイベントの ReplayID を記録しておくなどの方法が考えられます。

トランザクションの境界

発行者と登録者は、異なるトランザクションで実行されます。これにより、発行者の処理が成功した後に、登録者の処理が(たとえ失敗しても)発行者のトランザクションに影響を与えることはありません。この非同期性とトランザクションの分離が、システムの回復力を高める一方で、一貫性の担保には注意が必要です。結果整合性 (Eventual Consistency) を許容できる設計が求められます。


まとめとベストプラクティス

Platform Events は、Salesforce を中心としたエンタープライズシステムにおいて、疎結合でスケーラブルな Event-Driven Architecture を実現するための強力なツールです。正しく活用することで、システムの保守性を高め、ビジネスの変化に迅速に対応できる俊敏なアーキテクチャを構築できます。

ベストプラクティス:

  1. 明確なイベントスキーマを設計する: イベントのペイロードは、そのイベントを表現するために必要十分な情報を持つべきです。関連レコードの ID を含めるのが一般的ですが、購読者が毎回 Salesforce に問い合わせなくても済むように、重要な情報(ステータスなど)を含めることも検討します。
  2. イベントの粒度を考慮する: 細かすぎるイベントはシステムのノイズを増やし、大きすぎるイベントは柔軟性を損ないます。ビジネスプロセスにおける意味のある状態変化をイベントとしてモデル化します。
  3. - イベントスキーマのバージョニングを計画する: 将来的にイベントの項目を追加・変更する必要が出てくる可能性があります。後方互換性を維持するために、バージョン番号を示す項目をペイロードに含めるなどの戦略を検討します。
  4. 購読者の冪等性 (Idempotency) を確保する: ネットワークの問題などで同じイベントが複数回配信される可能性はゼロではありません。購読者側のロジックは、同じイベントを複数回処理しても問題が発生しないように、冪等に設計することが理想的です。例えば、処理済みのイベント ID を記録しておくなどの対策が考えられます。
  5. 適切なツールを選択する: 宣言的な処理で十分な場合はフローを、複雑なビジネスロジックが必要な場合は Apex トリガを、外部システムとの連携には MuleSoft や CometD クライアントを使用するなど、用途に応じて最適な購読方法を選択します。
  6. ガバナ制限を監視する: 特に大規模な実装では、イベントの使用状況を定期的に監視し、制限に近づいていないかを確認することが不可欠です。必要に応じて High-Volume Platform Events への移行やアーキテクチャの見直しを検討します。

Platform Events は単なる技術的な機能ではなく、システム設計の哲学そのものを変える可能性を秘めています。これを使いこなし、変化に強く、拡張性の高い Salesforce ソリューションを構築していきましょう。

コメント