Salesforce Platform Events を活用したリアルタイム連携の実現

背景と応用シナリオ

現代のエンタープライズシステム環境では、複数のシステムが連携し、データをリアルタイムで同期することが求められます。従来のポイントツーポイント連携、特にSOAPやREST APIを用いた同期的なリクエスト/レスポンスモデルは、システム間の結合度を高くしてしまいます。これは、一方のシステムがダウンしている場合や、API仕様の変更があった場合に、他方のシステムに直接的な影響を及ぼすという脆弱性を抱えています。

Salesforce 統合エンジニアとして、私たちはこのような密結合なアーキテクチャがもたらす課題、すなわちメンテナンス性の低下、スケーラビリティの欠如、そして変更に対する硬直性に日々直面しています。

ここで登場するのが、Event-Driven Architecture (EDA、イベント駆動型アーキテクチャ) という設計思想です。EDAは、システムの各コンポーネントが「イベント」と呼ばれる状態変化の通知を非同期に送受信することで連携するモデルです。このモデルでは、イベントの発行者 (Publisher) は、誰がそのイベントを購読 (Subscribe) しているかを知る必要がありません。この「疎結合」な関係性が、システムの柔軟性と拡張性を劇的に向上させます。

Salesforceが提供するEDAの実装こそが、Platform Events (プラットフォームイベント) です。Platform Eventsは、Salesforce内外のシステム間で、セキュアでスケーラブルなカスタム通知をリアルタイムに送受信するためのメッセージング基盤です。

具体的な応用シナリオ

  • リアルタイムデータ同期: Salesforceで「商談」が「成立」になった瞬間にPlatform Eventを発行します。外部のERPシステム(例: SAP, Oracle)がこのイベントを購読し、リアルタイムで受注伝票を自動作成します。APIを直接呼び出す場合に比べ、ERP側のシステムが一時的に利用できなくても、イベントは保持され(後述のReplay機能)、復旧後に処理を再開できます。
  • 外部システムへの通知: 「ケース」のステータスが「解決済み」に変更された際にイベントを発行し、顧客が利用する外部のサポートポータルに通知を送り、表示を更新します。これにより、顧客は常に最新の情報を得ることができます。
  • IoT連携: 工場に設置されたセンサーが異常な温度を検知した際に、そのデータをPlatform EventとしてSalesforceに送信します。Salesforce側では、Apexトリガーやフローがこのイベントを購読し、自動的に保守担当者へのToDo作成や、関連する取引先責任者への警告メール送信といったアクションを実行します。
  • 大規模な組織内プロセスの分離: 複雑なビジネスプロセスを複数の小さなプロセスに分割し、それぞれをPlatform Eventsで連携させます。例えば、注文が確定した際にイベントを発行し、「在庫引き当てプロセス」「請求書作成プロセス」「配送準備プロセス」がそれぞれ独立してそのイベントを購読し、並行して処理を進めることができます。これにより、各プロセスの独立性が保たれ、開発や改修が容易になります。

原理説明

Platform Eventsの仕組みを理解するためには、いくつかの主要なコンポーネントとその役割を把握する必要があります。

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

Platform Eventは、カスタムオブジェクトを定義するのと同様に、[設定]メニューから宣言的に作成します。API参照名は慣習的に `__e` で終わります(例: `Order_Update__e`)。カスタムオブジェクトと同様に、カスタム項目を定義して、イベントメッセージに含めるデータ(ペイロード)の構造を決定します。例えば、「注文更新イベント」であれば、「注文ID」「ステータス」「更新日時」といった項目を持つことになります。

2. 発行者 (Publisher)

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

  • Apex: `EventBus.publish()` メソッドを使用して、Apexコード内からプログラムでイベントを発行します。最も柔軟な方法です。
  • フロー (Flow): 「レコードを作成」要素で、定義したPlatform Eventオブジェクトのレコードを作成することで、宣言的にイベントを発行できます。
  • プロセスビルダー (Process Builder): フローと同様に、「レコードを作成」アクションでイベントを発行できます。(注: 新規の自動化にはフローの利用が推奨されています。)
  • API経由の外部システム: REST APIやSOAP API、Pub/Sub APIを利用して、Salesforceの外部にあるアプリケーションがイベントを発行することも可能です。これにより、外部システムをイベントの起点とすることができます。

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

Event Busは、発行された全てのイベントメッセージを一時的に保持し、購読者に配信するための中央チャネルです。これはSalesforceが管理するマルチテナント対応のメッセージング基盤であり、高いスケーラビリティと信頼性を提供します。発行されたイベントは時系列に並べられ、各イベントにはReplayId (再生ID) と呼ばれる一意のIDが割り振られます。

4. 購読者 (Subscriber)

Subscriberは、Event Busをリッスンし、特定のイベントが発生した際にそれを受信して、定義されたアクションを実行します。

  • Apex トリガー: Platform Eventオブジェクトに対して `after insert` トリガーを作成することで、イベントを受信した直後にApexコードを実行できます。
  • - フロー (Flow): 「プラットフォームイベントによってトリガーされるフロー」を作成し、イベント受信を起点に一連の自動化プロセスを宣言的に構築できます。
  • 外部アプリケーション (CometD / Pub/Sub API): CometDは、Bayeuxプロトコルをベースにしたロングポーリング技術で、外部クライアントがSalesforceのEvent Busに常時接続し、リアルタイムでイベントを購読することを可能にします。よりモダンで高性能なPub/Sub APIも利用可能です。MuleSoftなどの連携プラットフォームも、これらの仕組みを利用してイベントを購読します。

イベント発行から購読までの流れは、「発行 → イベントバス → 購読」という一方向のシンプルなものです。重要なのは、発行者は購読者の存在を一切意識しない点、そして一つのイベントを複数の異なる購読者が同時に受信できる点です。これにより、システム間の疎結合が実現されます。


示例代码

ここでは、Apexを使用してPlatform Eventを発行し、それをApexトリガーで購読する最も基本的なシナリオのコードを示します。この例では、`Cloud_News__e` という、ニュース速報を通知するためのPlatform Eventが事前に定義されているものとします。

1. ApexによるPlatform Eventの発行

以下のコードは、ニュースの内容をセットして `Cloud_News__e` イベントを発行するメソッドの例です。

// イベントのリストを作成します。複数のイベントを一度に発行することも可能です。
List<Cloud_News__e> newsEvents = new List<Cloud_News__e>();

// 発行するイベントのインスタンスを生成し、項目に値を設定します。
// これは sObject レコードを作成するのと非常によく似ています。
Cloud_News__e newsEvent = new Cloud_News__e(
    Location__c='Mountain View', 
    Urgent__c=true,
    News_Content__c='Salesforce Winter \'25 Release is now available!');
newsEvents.add(newsEvent);

// EventBus.publish() メソッドを呼び出してイベントを発行します。
// このメソッドは非同期に実行されるわけではなく、トランザクションのコミット時に発行が確定します。
List<Database.SaveResult> results = EventBus.publish(newsEvents);

// 発行結果をループで確認します。
// isSuccess() が true であれば、イベントは正常に発行キューに追加されています。
for (Database.SaveResult sr : results) {
    if (sr.isSuccess()) {
        System.debug('Successfully published event. Event ID: ' + sr.getId());
    } else {
        // エラーがあった場合の処理
        for(Database.Error err : sr.getErrors()) {
            System.debug('Error returned: ' +
                        err.getStatusCode() +
                        ' - ' +
                        err.getMessage());
        }
    }
}

2. ApexトリガーによるPlatform Eventの購読

`Cloud_News__e` イベントが発行された際に、その内容を元にケースを自動作成するApexトリガーの例です。トリガーはイベントオブジェクトに対して `after insert` で定義します。

// イベントオブジェクト Cloud_News__e に対するトリガーを定義します。
// Platform Event のトリガーは `after insert` のみサポートされます。
trigger CloudNewsTrigger on Cloud_News__e (after insert) {
    
    List<Case> casesToCreate = new List<Case>();

    // Trigger.new には、受信したイベントメッセージのリストが格納されています。
    // ループで各イベントを処理します。
    for (Cloud_News__e event : Trigger.new) {
        System.debug('Received news event: ' + event.News_Content__c);

        // 緊急 (Urgent__c) フラグが true のイベントのみを対象とします。
        if (event.Urgent__c == true) {
            // イベントのペイロードから情報を取り出し、新しいケースオブジェクトを作成します。
            Case newCase = new Case();
            newCase.Subject = 'Urgent News Received from ' + event.Location__c;
            newCase.Description = event.News_Content__c;
            newCase.Priority = 'High';
            newCase.Status = 'New';
            
            casesToCreate.add(newCase);
        }
    }

    // DML操作はループの外で行うのがベストプラクティスです。
    // これにより、ガバナ制限(Governor Limits)に抵触するリスクを低減します。
    if (!casesToCreate.isEmpty()) {
        try {
            insert casesToCreate;
        } catch (DmlException e) {
            System.debug('An error occurred while inserting cases: ' + e.getMessage());
            // ここでエラーハンドリングロジックを実装します (例: カスタムログオブジェクトへの記録)。
        }
    }
}

注意事項

Platform Eventsを本番環境で活用する際には、以下の点に注意する必要があります。

権限 (Permissions)

イベントを発行または購読するユーザーや連携APIユーザーのプロファイルまたは権限セットには、対象のPlatform Eventオブジェクト (`__e`) に対する「作成」権限(発行のため)および「参照」権限(購読のため)が必要です。

API制限 (API Limits)

Platform Eventsには、エディションやライセンスによって定義されたガバナ制限が存在します。

  • イベント発行の制限 (Publishing Limits): 24時間以内に発行できるイベントの数には上限があります。この上限は、Standard-Volume Platform EventsHigh-Volume Platform Events (ハイボリュームプラットフォームイベント) のどちらを利用するかによって異なります。High-Volumeはより多くのイベントを発行できますが、発行の挙動が若干異なります(後述)。`Limits.getPublishingLimit()` Apexメソッドや、REST APIのLimitsリソースで現在の使用状況を確認できます。
  • イベント配信の制限 (Delivery Limits): CometDクライアントなどの外部購読者に配信されるイベント数にも上限があります。通常、この上限は非常に大きいですが、大規模な連携を設計する際には考慮が必要です。

イベントの保持とReplayId

発行されたイベントは、Event Busに24時間(High-Volumeの場合は72時間)保持されます。外部の購読者システムは、ネットワーク障害などで一時的にイベントを受信できなくなった場合でも、最後に正常に処理したイベントの ReplayId を保存しておくことで、切断時点以降のイベントを再取得(リプレイ)することができます。これは、メッセージの欠損を防ぐための非常に重要な機能です。

エラー処理 (Error Handling)

  • 発行時のエラー: `EventBus.publish()` の戻り値である `Database.SaveResult` を必ず確認し、発行が失敗した際の原因を特定し、リトライや通知などのロジックを実装すべきです。
  • 購読時のエラー: Apexトリガー内で例外が発生した場合、そのトリガーのトランザクションはロールバックされますが、イベントの発行元や他の購読者のトランザクションには影響を与えません。 これが疎結合の利点です。ただし、エラーが発生したイベントは再処理されないため、トリガー内には堅牢な `try-catch` ブロックを実装し、エラー内容をカスタムログオブジェクトに記録するなどして、問題を追跡できるようにすることが不可欠です。

トランザクションの境界

Apexトランザクション内で `EventBus.publish()` を呼び出した場合、イベントが実際に発行されるのは、そのトランザクション全体が正常にコミットされた後です。これにより、データベースへの書き込みがロールバックされたにもかかわらず、イベントだけが発行されてしまうといったデータ不整合を防ぐことができます。


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

Platform Eventsは、Salesforceを中心としたシステム連携アーキテクチャを、従来の密結合なポイントツーポイント型から、柔軟でスケーラブルなイベント駆動型へと変革するための強力なツールです。

Salesforce 統合エンジニアとして、Platform Eventsを最大限に活用するためのベストプラクティスを以下に示します。

  1. 明確なイベントスキーマを設計する: Platform Eventの項目定義は、システム間の「契約(Contract)」です。イベント名や項目名は、そのイベントが何を表すのかが明確にわかるように命名し、一度定義したら安易に変更しないようにします。変更が必要な場合は、バージョン情報を含む項目を追加するなど、後方互換性を意識した設計を心がけます。
  2. 適切なイベントタイプを選択する:
    • Standard-Volume Events: トランザクションのコミットと同時に発行が保証されるため、即時性が求められるビジネスプロセスに適しています。ただし、発行数に厳しい制限があります。
    • High-Volume Events: 大量のイベント(数百万件/日レベル)を処理するために設計されています。発行は非同期キューイングを経て行われるため、わずかな遅延が発生する可能性がありますが、スループットが格段に向上します。IoTデータやログの連携など、大量のデータを扱う場合に選択します。
  3. 外部購読者にはReplay機能の実装を徹底する: 外部システム側でイベントを購読する場合は、必ず最後に処理した `ReplayId` を永続化する仕組みを実装してください。これにより、システム停止やネットワーク障害からの復旧時に、メッセージロスなく処理を再開できます。
  4. 購読者ロジックは軽量に保つ: Apexトリガーやフローは、イベントを迅速に処理し、次のイベントに備えるべきです。時間のかかる複雑な処理(外部APIコールアウトや複雑な計算など)は、購読者ロジック内で直接実行せず、イベントのペイロードを元にプラットフォームキャッシュに保存したり、Queueable Apexや非同期実行のフローなどの非同期処理を起動して、後続の処理に引き渡すように設計します。
  5. イベント使用状況を監視する: Salesforceの [設定] > [プラットフォームイベント] ページや、`PlatformEventUsageMetric` オブジェクトへのSOQLクエリを通じて、イベントの発行・配信状況を定期的に監視し、制限に近づいていないかを確認する運用を組み込みます。

これらのプラクティスを遵守することで、Platform Eventsの真価を引き出し、堅牢で保守性の高い、未来志向の連携ソリューションを構築することができるでしょう。

コメント