Salesforce Platform Events をマスターする:堅牢なシステム連携のためのガイド

背景と応用シナリオ

Salesforce 統合エンジニアとして、私は日々、Salesforce と外部システム間のデータ連携という課題に取り組んでいます。従来のポイント・ツー・ポイントの API 連携は、一見シンプルですが、システムが増えるにつれて複雑性が増し、結合度が密になりすぎる(Tightly Coupled)という問題がありました。一方のシステムの変更が、他方のシステムに予期せぬ影響を与えるリスクを常に抱えています。

この課題を解決するのが、Event-Driven Architecture (イベント駆動型アーキテクチャ) です。これは、システムの各コンポーネントが「イベント」を介して非同期に通信する設計思想です。そして、Salesforce がこのアーキテクチャを実現するために提供するネイティブ機能が Platform Events (プラットフォームイベント) です。

Platform Events は、Salesforce 内外で発生した「意味のある出来事」をメッセージとしてブロードキャストするための仕組みです。イベントの発行者(Producer)は、受信者(Subscriber)を意識する必要がありません。ただイベントバスにメッセージを投げるだけで、関心のある受信者がそれを自律的に受け取ります。これにより、システム間の疎結合(Decoupling)が実現され、拡張性と保守性に優れたアーキテクチャを構築できます。

具体的な応用シナリオ

  • リアルタイムデータ同期:外部の ERP システムで注文ステータスが「発送済み」に変更された際、Platform Event を発行します。Salesforce 側のサブスクライバーがこれを検知し、関連する商談や注文オブジェクトのステータスをリアルタイムに更新します。
  • IoT デバイス連携:工場のセンサーが異常値を検出した際に Platform Event を発行し、Salesforce 上で保守ケースを自動作成します。
  • 複数システムへの通知:Salesforce で重要な取引先情報が更新された際、その変更内容を Platform Event としてブロードキャストします。データウェアハウス、マーケティングオートメーションツール、監査ログシステムなど、複数のサブスクライバーがそれぞれ必要な処理を並行して実行できます。

原理説明

Platform Events の仕組みは、3つの主要なコンポーネントで構成されています。

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

まず、どのような情報をイベントとしてやり取りするかを定義します。これは、Salesforce のカスタムオブジェクトを作成するのと非常によく似ています。「設定」から「プラットフォームイベント」に移動し、API 参照名が __e で終わるイベントオブジェクトを定義します。ここには、イベントで伝えたい情報に対応するカスタム項目(テキスト、数値、チェックボックスなど)を定義します。例えば、「注文発送イベント」なら、「注文番号」「追跡番号」「発送日」といった項目が含まれるでしょう。

2. イベントの発行 (Event Publishing)

イベントの発行者(Event Producer)が、定義されたイベントオブジェクトのインスタンスを作成し、イベントバスに送信します。発行方法は多岐にわたります。

  • Apex: EventBus.publish() メソッドを使用。
  • フロー (Flow): 「レコードを作成」要素でイベントオブジェクトのレコードを作成。
  • プロセスビルダー (Process Builder): アクションでイベントを発行。(新規作成はフローが推奨)
  • Salesforce APIs: REST API や SOAP API を使って外部システムから直接イベントを発行。
発行されたイベントは、Salesforce のスケーラブルなマルチテナント型メッセージング基盤である Event Bus (イベントバス) に送られます。

3. イベントの購読 (Event Subscription)

イベントの受信者(Event Subscriber)は、特定のイベントをリッスン(購読)します。イベントが発生すると、イベントバスからリアルタイムに通知を受け取ります。購読方法も複数存在します。

  • Apex トリガ: イベントオブジェクト(__e で終わるオブジェクト)に対して after insert トリガを作成して処理を実行。
  • フロー (Flow): 「プラットフォームイベントによってトリガされるフロー」を作成。
  • CometD クライアント: MuleSoft やカスタムの Web アプリケーションなど、Salesforce の外部クライアントが CometD というプロトコルを使ってイベントを購読。これは外部システム連携の要です。
  • LWC (Lightning Web Components): lightning/empApi モジュールを使用して、UI 上でリアルタイムにイベントを反映。
この発行者と受信者が分離されている点が、Platform Events の最も強力な特徴です。


サンプルコード

ここでは、Apex を使用して Platform Event を発行し、Apex トリガで購読する最も一般的なシナリオを見ていきます。

1. プラットフォームイベントの定義

まず、[設定] > [統合] > [プラットフォームイベント] で、以下のようなイベントを定義したと仮定します。

  • 表示ラベル: Cloud News Event
  • API 参照名: Cloud_News__e
  • カスタム項目:
    • Location__c (Text)
    • Urgent__c (Checkbox)
    • News_Content__c (Long Text Area)

2. Apex によるイベントの発行

以下のコードは、Cloud_News__e イベントのリストを作成し、EventBus.publish() メソッドを使用して一度に発行します。

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

// 1つ目のイベントインスタンスを作成し、項目値を設定
Cloud_News__e newsEvent1 = new Cloud_News__e(
    Location__c='West', 
    Urgent__c=true, 
    News_Content__c='Major storm in the West region.'
);
newsEvents.add(newsEvent1);

// 2つ目のイベントインスタンスを作成
Cloud_News__e newsEvent2 = new Cloud_News__e(
    Location__c='East', 
    Urgent__c=false, 
    News_Content__c='Clear skies in the East region.'
);
newsEvents.add(newsEvent2);

// EventBus.publish() を呼び出して、リスト内のすべてのイベントを発行
// このメソッドは、トランザクションが正常にコミットされた後にイベントを非同期で発行します
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());
        }
    }
}

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

発行された Cloud_News__e イベントを購読し、処理を実行する Apex トリガです。プラットフォームイベントのトリガは、常に after insert コンテキストで動作します。

// Cloud_News__e プラットフォームイベントに対するトリガを定義
// イベントの購読は常に 'after insert' で行われる
trigger CloudNewsTrigger on Cloud_News__e (after insert) {

    // 受け取ったイベントのリストを処理するために、Task のリストを初期化
    List<Task> tasks = new List<Task>();

    // Trigger.New には、受信したイベントメッセージのリストが格納されている
    // これをループ処理して、各イベントに対するアクションを実行する
    for (Cloud_News__e event : Trigger.New) {
        System.debug('Received news event for location: ' + event.Location__c);

        // 緊急 (Urgent__c) フラグが true のイベントのみを処理対象とする
        if (event.Urgent__c == true) {
            // 緊急のニュースに対応するための Task レコードを作成
            Task tk = new Task();
            tk.Priority = 'High';
            tk.Subject = 'Follow up on urgent news report: ' + event.Location__c;
            tk.OwnerId = UserInfo.getUserId(); // タスクの所有者を現在のユーザに設定
            tasks.add(tk);
        }
    }

    // 作成する Task レコードが存在する場合にのみ、DML 操作を実行
    if (!tasks.isEmpty()) {
        insert tasks;
    }
}

注意事項

Platform Events を本番環境で活用する際には、いくつかの重要な点を考慮する必要があります。

権限 (Permissions)

ユーザーが Platform Event を利用するには、プロファイルまたは権限セットで適切な権限が必要です。

  • 定義:「アプリケーションのカスタマイズ」権限。
  • 発行:イベントオブジェクト(Cloud_News__e など)に対する「作成」権限。
  • 購読:イベントオブジェクトに対する「参照」権限。

API 制限 (API Limits)

Platform Events は、ガバナ制限とは別に、独自の使用量ベースの制限があります。これらは組織のエディションによって異なります。

  • イベント発行の割り当て (Publishing Allocation): 24時間以内に発行できるイベントの最大数です。Standard-Volume EventsHigh-Volume Events で上限が異なります。High-Volume Events はアドオンライセンスが必要ですが、より高いスループットを提供します。インテグレーションの要件に応じて適切なタイプを選択する必要があります。
  • イベント配信の割り当て (Delivery Allocation): 24時間以内に CometD クライアントなどのサブスクライバーに配信できるイベントの最大数です。Apex トリガやフローによる内部購読はこの制限を消費しません。
  • 同時 CometD クライアント数: 同時に接続できる外部サブスクライバーの数にも上限があります。
これらの制限は、「組織情報」の「使用量ベースのエンタイトルメント」で確認できます。大規模な連携を設計する際は、これらの制限を常に意識する必要があります。

エラー処理と再試行 (Error Handling & Retry)

Platform Events の疎結合性は、エラーハンドリングにも影響します。

  • 発行時のエラー: EventBus.publish() の戻り値である Database.SaveResult を確認することで、発行が成功したか失敗したかを判断できます。
  • 購読時のエラー: あるサブスクライバーの Apex トリガがエラーで失敗しても、イベントの発行自体はロールバックされません。また、他のサブスクライバーの処理にも影響を与えません。トリガ内では堅牢な try-catch ブロックを実装し、エラーを適切に記録することが重要です。
  • イベントのリプレイ (Event Replay): ネットワーク障害などでサブスクライバーが一時的に停止した場合、復旧後に見逃したイベントを取得する仕組みがあります。各イベントには ReplayId (リプレイID) という一意のシーケンス番号が付与されており、サブスクライバーは最後に処理した ReplayId を指定することで、それ以降のイベントを再取得できます。イベントは通常24時間保持されます。

トランザクション境界 (Transaction Boundaries)

重要な点として、EventBus.publish() で発行されたイベントは、デフォルトでトランザクションが正常にコミットされた後にバスに送信されます(Publish After Commit)。これにより、DML 操作がロールバックされた場合に、実際には存在しないデータの変更を通知してしまう、という事態を防げます。


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

Salesforce Platform Events は、現代の複雑なシステム連携において、疎結合でスケーラブルなアーキテクチャを実現するための強力なツールです。統合エンジニアとして、私は以下のベストプラクティスを推奨します。

  1. イベントスキーマを API 契約として扱う: イベントの項目は、システム間の API 契約です。将来の拡張性を考慮し、明確で自己記述的な項目名を付け、バージョン管理の考え方を取り入れることを検討してください。
  2. 適切なイベントボリュームを選択する: トランザクション数が少ないが確実な配信が求められる場合は Standard-Volume、大量のデータをリアルタイムにストリーミングする場合は High-Volume を選択します。
  3. サブスクライバーのロジックを軽量に保つ: Apex トリガ内の処理は可能な限りシンプルにし、複雑なビジネスロジックは Queueable Apex や非同期処理に委譲することを検討してください。これにより、イベント処理の遅延やガバナ制限違反のリスクを低減できます。
  4. 外部サブスクライバーに再試行ロジックを実装する: 外部の購読クライアントには、接続断に備えて ReplayId を利用したイベントの再取得ロジックと、指数バックオフなどの再試行メカニズムを必ず実装してください。
  5. 監視とアラートを設定する: Platform Events の使用量を定期的に監視し、割り当て上限に近づいた際にアラートを出す仕組みを構築することが、安定した運用には不可欠です。

Platform Events を正しく理解し活用することで、変化に強く、拡張しやすい、堅牢な統合ソリューションを構築することが可能になります。

コメント