Salesforce Connectをマスターする:シームレスな外部データ統合ガイド

背景と適用シナリオ

Salesforceインテグレーションエンジニアとして、私たちの主な責務の一つは、サイロ化されたシステム間のデータを繋ぎ、統一されたビジネスプロセスを構築することです。現代の企業では、顧客データはSalesforceに、注文履歴はERPシステムに、製品在庫はサプライチェーン管理(SCM)システムに、そして財務データは別のレガシーデータベースに、といった具合にデータが分散していることがよくあります。

従来、これらのデータをSalesforceで活用するためには、ETL (Extract, Transform, Load) ツールを用いて夜間バッチでデータをコピー・同期する方法が一般的でした。しかし、このアプローチにはいくつかの課題が伴います。第一に、データの鮮度の問題です。バッチ処理では、Salesforce上のデータは常に数時間から1日古いものとなり、リアルタイムのビジネス判断には使えません。第二に、ストレージコストです。大量のデータをSalesforceに複製することは、高額なデータストレージ費用を発生させます。最後に、データの一貫性の維持が複雑になるという点です。

ここで登場するのが Salesforce Connect です。Salesforce Connect は、Salesforceの「データ仮想化」または「データフェデレーション」ソリューションであり、外部システムに保存されているデータをSalesforce内にコピーすることなく、リアルタイムで表示、検索、変更することを可能にします。これにより、あたかもSalesforceの標準オブジェクトやカスタムオブジェクトのように、外部データをシームレスに扱うことができます。

具体的な適用シナリオ:

  • 360度の顧客ビュー: 取引先責任者のページで、ERPシステムからリアルタイムの注文履歴や請求情報に直接アクセスする。
  • リアルタイム在庫確認: 営業担当者が商談を作成する際に、外部の在庫管理システムから最新の製品在庫状況を確認し、正確な納期を顧客に提示する。
  • レガシーシステムの統合: 簡単には移行できない古いメインフレームやカスタムデータベース上のデータを、Salesforceの最新のUI(Lightning Experience)を通じてサービス担当者が参照・更新できるようにする。
  • 複数組織のデータ集約: グローバル企業が地域ごとに持つ複数のSalesforce組織のデータを、本社組織から一元的に参照・レポートする。

私たちインテグレーションエンジニアにとって、Salesforce Connectは、データ同期の複雑さを排除し、常に最新のデータソースを信頼できる唯一の情報源(Single Source of Truth)として維持しながら、強力な統合ソリューションを迅速に構築するための重要なツールなのです。


原理の説明

Salesforce Connectの魔法は、2つの主要なコンポーネント、External Data Source (外部データソース) と External Object (外部オブジェクト) によって実現されます。

1. External Data Source (外部データソース):
これは外部システムへの接続設定を定義するものです。接続先のURL、認証方式(例: OAuth 2.0, パスワード認証)、使用するアダプタの種類などを指定します。Salesforceはこの設定を元に、外部システムとの通信方法を決定します。

2. External Object (外部オブジェクト):
これはSalesforceのカスタムオブジェクトに似たメタデータですが、データをSalesforce内に保存する代わりに、外部データソースのテーブルやエンドポイントにマッピングされます。ユーザが外部オブジェクトのレコードを表示しようとしたり、SOQLクエリを実行したりすると、Salesforceプラットフォームは舞台裏で次の処理をリアルタイムで行います。

  1. ユーザのアクション(レコード表示、検索など)を検知します。
  2. アクションを解釈し、対応するクエリを生成します(例: `SELECT Id, Name FROM ErpOrders__x WHERE AccountId__c = '001...'`)。
  3. 外部データソースで定義されたアダプタを使って、このクエリを外部システムが理解できる形式(例: ODataクエリ)に変換します。
  4. 変換されたクエリを外部システムに送信します。
  5. 外部システムから返されたデータを受信し、SalesforceのUIで表示できる形式に整形します。
  6. ユーザに結果を表示します。

この一連の流れは非常に高速に行われるため、ユーザはそれが外部データであると意識することなく操作できます。

アダプタの種類

Salesforce Connectは、さまざまな外部システムに接続するために、いくつかのアダプタを提供しています。

  • OData 2.0 / 4.0 Adapter: 最も一般的に使用されるアダプタです。OData (Open Data Protocol) は、REST APIを構築・利用するためのISO/IEC承認の標準規格です。多くの最新ERPやSaaSアプリケーションがODataをサポートしており、このアダプタを使えばコーディングなしで簡単に接続できます。
  • Cross-Org Adapter: 他のSalesforce組織のデータにアクセスするためのアダプタです。Salesforce間のデータ連携を非常にシンプルに実現します。
  • Apex Custom Adapter: 接続先のシステムがODataをサポートしていない場合に、私達インテグレーションエンジニアの腕の見せ所となります。Apex Connector Framework を使用して、Apexコードで独自のアダプタを開発します。これにより、独自のREST API、SOAP API、さらにはデータベースに直接接続するなど、ほぼあらゆるデータソースへの接続が可能になります。

サンプルコード:Apexカスタムアダプタの実装

外部システムが標準のODataをサポートしていない場合、Apexカスタムアダプタを開発する必要があります。ここでは、シンプルな製品情報を返す外部のREST APIに接続するためのカスタムアダプタの `DataSource.Connection` クラスの実装例を示します。このコードは、クエリが実行された際に外部APIを呼び出し、返されたJSONデータを解析して外部オブジェクトの行データに変換するロジックを含んでいます。

この例では、`query` メソッドに焦点を当てます。このメソッドは、Salesforceが外部オブジェクトに対してクエリ(例:リストビューの表示やSOQL実行)を行うたびに呼び出されます。

// ProductDataSourceConnection.cls
// 外部システムへの実際の接続とデータ操作を処理するクラス
public class ProductDataSourceConnection extends DataSource.Connection {

    // 外部データソースのメタデータを保持
    private DataSource.ConnectionParams connectionInfo;

    // コンストラクタ
    public ProductDataSourceConnection(DataSource.ConnectionParams connectionInfo) {
        this.connectionInfo = connectionInfo;
    }

    // クエリが実行されたときに呼び出されるメソッド
    override public DataSource.QueryUtils.Result query(DataSource.QueryContext context) {
        // HTTPリクエストを作成
        HttpRequest req = new HttpRequest();
        // 外部データソースで設定されたエンドポイントURLを取得
        // Named Credentialの使用を推奨
        req.setEndpoint(this.connectionInfo.endpoint + '/products');
        req.setMethod('GET');
        req.setHeader('Accept', 'application/json');

        try {
            // HTTPコールアウトを実行
            HttpResponse res = new Http().send(req);
            
            // ステータスコードが200 (OK) 以外の場合はエラーをスロー
            if (res.getStatusCode() != 200) {
                throw new CalloutException('API Error: ' + res.getStatus() + ' - ' + res.getBody());
            }

            // レスポンスのJSONボディを解析
            List<Object> results = (List<Object>) JSON.deserializeUntyped(res.getBody());
            
            // 結果を格納するテーブルデータを初期化
            DataSource.TableResult result = new DataSource.TableResult();
            List<DataSource.Row> rows = new List<DataSource.Row>();

            // 取得した各製品データをループ処理
            for (Object obj : results) {
                Map<String, Object> productData = (Map<String, Object>) obj;

                // 1行分のデータを作成
                DataSource.Row row = DataSource.Row.create();
                
                // 外部システムの列名と値をマッピング
                // 'ExternalId' は外部オブジェクトで「外部ID」として設定された項目
                row.addCell('ExternalId', (String) productData.get('productId'));
                // 'DisplayUrl' は外部オブジェクトで「表示URL」として設定された項目
                row.addCell('DisplayUrl', (String) productData.get('productUrl'));
                
                // カスタム項目へのマッピング
                row.addCell('Name', (String) productData.get('name'));
                row.addCell('Price__c', (Decimal) productData.get('price'));
                row.addCell('StockQuantity__c', (Integer) productData.get('stock'));

                rows.add(row);
            }
            
            // 構築した行データをテーブル結果に設定
            result = DataSource.TableResult.success(context.tableSelection, rows);
            return result;

        } catch (Exception e) {
            // 例外が発生した場合、エラーを返す
            return DataSource.TableResult.error(context.tableSelection, 'An error occurred: ' + e.getMessage());
        }
    }

    // sync、upsertRows、deleteRows などの他のメソッドも必要に応じてオーバーライド可能
    override public List<DataSource.Table> sync() {
        // このメソッドは外部データソースの「検証して同期」をクリックした際に呼び出される
        // 外部システムのテーブルスキーマを定義して返す
        List<DataSource.Table> tables = new List<DataSource.Table>();
        List<DataSource.Column> columns = new List<DataSource.Column>();
        
        columns.add(DataSource.Column.text('Name', 255));
        columns.add(DataSource.Column.url('DisplayUrl'));
        columns.add(DataSource.Column.number('Price__c', 18, 2));
        columns.add(DataSource.Column.number('StockQuantity__c', 10, 0));
        
        // 外部オブジェクトのテーブル名と列を定義
        tables.add(DataSource.Table.create('Products', 'ExternalId', columns));
        
        return tables;
    }
}

注: このコードは `DataSource.Connection` の実装例です。実際にカスタムアダプタを機能させるには、どのConnectionクラスを使用するかを定義する `DataSource.Provider` クラスも実装する必要があります。


注意事項

Salesforce Connectは非常に強力ですが、万能ではありません。インテグレーションエンジニアとして、その制限を正確に理解し、顧客に説明する責任があります。

パフォーマンスとガバナ制限

  • コールアウト制限: Salesforce Connectによる外部システムへのアクセスは、Salesforceの外部コールアウト制限の対象となります。組織全体で1時間あたりに実行できるコールアウトの数には上限があります(組織のエディションにより異なる)。高頻度でアクセスされる外部オブジェクトは、この制限に達するリスクがあります。
  • タイムアウト: 外部システムからの応答が遅い場合、Salesforceはタイムアウトします(最大120秒)。外部システムのパフォーマンスが、Salesforceユーザの体験に直接影響します。
  • データ量: 一度のクエリで大量のデータを取得しようとすると、パフォーマンスが低下したり、タイムアウトやヒープサイズ制限に達する可能性があります。カスタムアダプタでは、ページネーション(データの分割取得)の実装が不可欠です。

データモデルと機能の制限

  • サポートされない機能: 外部オブジェクトは、Salesforceの標準・カスタムオブジェクトのすべての機能をサポートしているわけではありません。例えば、Apexトリガ、ワークフロールール、入力規則、積み上げ集計項目などは使用できません。
  • リレーションシップ: 外部オブジェクトは、標準・カスタムオブジェクトとの参照関係(Indirect Lookup, External Lookup)を持つことができますが、主従関係の主(Master)になることはできません。
  • レポートとダッシュボード: 外部オブジェクトをレポートに含めることは可能ですが、他のオブジェクトとの結合には制限があります。パフォーマンス上の理由から、大規模なデータセットに対する複雑なレポート作成には向いていません。

セキュリティと認証

  • 認証設定: 外部データソースでは、「匿名」「ユーザ単位」「指定ユーザ」の3つの認証方式を選択できます。どの方式を選択するかは、外部システムのセキュリティモデルとビジネス要件に依存します。「ユーザ単位」を選択した場合、各ユーザは外部システムの認証情報を個別に設定する必要があります。
  • Named Credentials (指定ログイン情報): エンドポイントURLや認証情報をコード内にハードコーディングするのではなく、必ず「指定ログイン情報」を使用してください。これにより、認証情報の管理が容易になり、セキュリティが向上します。

書き込み操作とデータ一貫性

  • 書き込みの有効化: 外部オブジェクトのデータをSalesforceから作成・編集・削除するには、外部データソースで「書き込み可能な外部オブジェクト」を有効にする必要があります。また、接続先の外部システム(ODataエンドポイントやカスタムAPI)が、POST, PATCH, DELETEといった書き込み操作をサポートしている必要があります。
  • トランザクション管理: SalesforceのDML操作と外部システムの書き込み操作は、別々のトランザクションとして扱われます。つまり、Salesforceのレコード更新が成功しても、外部システムへのコールアウトが失敗する可能性があります。このため、データの一貫性を保つための堅牢なエラーハンドリングとリカバリ戦略が重要になります。

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

Salesforce Connectは、データを複製することなく、外部システムのデータをリアルタイムでSalesforceに統合するための画期的なソリューションです。これにより、常に最新のデータに基づいた意思決定を支援し、ストレージコストを削減し、統合開発の複雑さを軽減することができます。

インテグレーションエンジニアとしてSalesforce Connectを成功させるためには、以下のベストプラクティスを心掛けることが重要です。

  1. ユースケースを慎重に選択する: Salesforce Connectは、リアルタイムで少量のデータをオンデマンドで参照するシナリオに最適です。大量データのバッチ処理やデータウェアハウスへの集約が目的の場合は、MuleSoftやInformaticaなどのETLツールが依然として適切な選択肢です。
  2. 外部システムを深く理解する: 統合プロジェクトを開始する前に、接続先システムのAPIの仕様、パフォーマンス、レート制限、セキュリティモデルを徹底的に調査してください。これがプロジェクトの成否を分けます。
  3. 可能な限りODataを優先する: 外部システムがODataをサポートしている場合は、標準のODataアダプタを使用することを強く推奨します。これにより、開発・保守コストを大幅に削減できます。
  4. ページネーションとクエリの最適化: 特にカスタムアダプタを開発する際は、必ずページネーションを実装し、フィルタ(WHERE句)を活用して、一度に取得するデータ量を最小限に抑えるように設計してください。
  5. 堅牢なエラーハンドリング: 外部システムはいつでも利用不能になる可能性があります。カスタムアダプタ内では、タイムアウト、接続エラー、APIからのエラーレスポンスなどを適切に捕捉し、ユーザに分かりやすいフィードバックを返すロジックを実装してください。
  6. 徹底的なテスト: 機能テストに加えて、負荷テストやパフォーマンステストを実施し、実際の利用状況下でシステムが期待通りに動作することを確認してください。特に、多数のユーザが同時にアクセスした場合の外部システムの応答性を検証することが重要です。

これらの原則に従うことで、Salesforce Connectの能力を最大限に引き出し、ビジネスに真の価値をもたらす、安定かつ高性能な統合ソリューションを構築することができるでしょう。

コメント