Salesforce 外部オブジェクトをマスターする:シームレスなデータ統合の実現


背景と応用シナリオ

現代の企業システムにおいて、データは様々な場所に分散して存在しています。Salesforce は顧客関係管理(CRM)の中心ですが、注文履歴は ERP システムに、製品在庫は倉庫管理システムに、あるいは古い顧客データはレガシーなデータベースに格納されている、といったケースは珍しくありません。これらのサイロ化されたデータを統合するために、従来は ETL (Extract, Transform, Load) プロセスが主流でした。しかし、ETL はデータのコピーを作成するため、データの鮮度が失われたり、ストレージコストが増大したり、同期プロセスの開発・保守に多大な工数がかかるという課題がありました。

この課題を解決するのが、Salesforce Connect を利用した External Objects (外部オブジェクト) です。External Objects は、Salesforce 内にデータを物理的に保存することなく、外部システムにあるデータにリアルタイムでアクセスし、あたかも Salesforce の標準オブジェクトやカスタムオブジェクトのように表示・操作できる機能です。これは「データ仮想化」とも呼ばれるアプローチで、データのコピーを不要にし、常に最新の情報をユーザーに提供します。

主な応用シナリオ

  • 360度の顧客ビュー:取引先責任者ページに、外部の ERP システムから取得したリアルタイムの注文履歴や請求情報を表示する。
  • 在庫確認:商談や商品ページで、外部の在庫管理システムに直接問い合わせ、最新の在庫状況を確認する。
  • データコンプライアンス:特定の国や地域の規制により、データを Salesforce のサーバーに物理的に保存できない場合でも、現地のデータベースにあるデータを参照・利用する。
  • レガシーシステムの活用:大規模な改修が難しいレガシーシステム上のデータを、最新の Salesforce UI/UX を通じて活用する。

原理説明

External Objects の中核をなすのは Salesforce Connect というアドオン機能です。Salesforce Connect は、外部データソースへの接続を定義し、そのデータソース内のテーブルやエンティティを Salesforce の External Objects としてマッピングする役割を担います。

ユーザーが External Object のレコード詳細ページを開いたり、レポートを実行したりすると、Salesforce はそのリクエストをリアルタイムで外部システムへのクエリに変換して送信します。外部システムはそのクエリを処理し、結果を Salesforce に返します。Salesforce は受け取ったデータを即座に画面に表示します。この一連のプロセスは透過的に行われるため、ユーザーはそれが外部データであると意識することなく操作できます。

主要なコンポーネント

1. External Data Source (外部データソース)
これは外部システムへの接続設定そのものです。接続プロトコル、エンドポイント URL、認証情報などを定義します。Salesforce Connect は主に以下の接続方法をサポートしています。

  • OData 2.0 / 4.0: Open Data Protocol (OData) は、REST API の標準規格の一つです。多くのモダンな ERP やデータベースは OData をサポートしており、コーディングなしで接続できる最も一般的な方法です。
  • Cross-Org: 別の Salesforce 組織のデータに接続するためのアダプタです。
  • Custom Adapter (Apex Connector Framework): OData をサポートしていない独自の API やデータベースに接続するためのフレームワークです。Apex を用いて独自のコネクタを開発することで、あらゆるデータソースに接続できる高い柔軟性を提供します。

2. External Objects (外部オブジェクト)
External Data Source を定義した後、「検証して同期」ボタンをクリックすると、外部システムのテーブルやエンティティの一覧が表示されます。ここから Salesforce にマッピングしたいものを選択すると、対応する External Object が自動的に作成されます。これは Salesforce 上のメタデータ定義であり、フィールドやページレイアウトなどを通常のオブジェクトと同様にカスタマイズできますが、データそのものは保持しません。


示例代码

OData をサポートしない独自のシステムに接続する場合、Apex Connector Framework を使用してカスタムアダプタを開発します。以下は、developer.salesforce.com の公式ドキュメントに記載されている、シンプルなカスタムコネクタの実装例です。このコネクタは、架空のデータソースに接続するための基本的な構造を示しています。

カスタムコネクタは、主に DataSource.ProviderDataSource.Connection の2つのクラスで構成されます。

1. Provider クラス

このクラスは、データソースの種類と認証機能を定義します。

// DataSource.Provider を実装し、コネクタの基本情報を定義するクラス
public class SimpleConnectorProvider implements DataSource.Provider {
    // 認証機能のタイプを返す
    // ここでは基本的なパスワード認証を指定
    override public List<DataSource.AuthenticationCapability> getAuthenticationCapabilities() {
        List<DataSource.AuthenticationCapability> capabilities =
            new List<DataSource.AuthenticationCapability>();
        capabilities.add(DataSource.AuthenticationCapability.BASIC);
        return capabilities;
    }

    // データソースの接続フォームに必要な項目を定義する
    // ここでは「Server URL」という項目を追加している
    override public List<DataSource.ConfigurationForm> getConfigurationForms() {
        List<DataSource.ConfigurationForm> configurationForms =
            new List<DataSource.ConfigurationForm>();
        DataSource.ConfigurationForm serverForm = new DataSource.ConfigurationForm();
        serverForm.addFormItem(
            'Server URL', 'url', DataSource.ConfigurationFormItemType.URL, true);
        configurationForms.add(serverForm);
        return configurationForms;
    }

    // データソースの種類を返す
    // ここでは 'Simple' というカスタムタイプを指定
    override public DataSource.Connection getConnection(DataSource.ConnectionParams
        connectionParams) {
        return new SimpleConnectorConnection(connectionParams);
    }

    // データソースの機能を定義する
    // ここでは、行番号によるページネーションをサポートすることを宣言
    override public List<DataSource.Capability> getCapabilities() {
        List<DataSource.Capability> capabilities = new List<DataSource.Capability>();
        capabilities.add(DataSource.Capability.ROW_QUERY);
        return capabilities;
    }
}

2. Connection クラス

このクラスが、実際のデータ操作(同期、クエリ、検索)のロジックを実装する心臓部です。

// DataSource.Connection を実装し、実際のデータアクセスロジックを記述するクラス
public class SimpleConnectorConnection implements DataSource.Connection {
    private DataSource.ConnectionParams connectionInfo;

    // コンストラクタで接続情報を受け取る
    public SimpleConnectorConnection(DataSource.ConnectionParams connectionInfo) {
        this.connectionInfo = connectionInfo;
    }

    // '検証して同期' を実行した際の処理
    // 外部システムのスキーマ(テーブルやカラム)を読み取り、Salesforceに返す
    override public DataSource.Schema aync() {
        DataSource.Schema schema = new DataSource.Schema();
        List<DataSource.Table> tables = new List<DataSource.Table>();

        // 'orders' という外部テーブルを定義
        List<DataSource.Column> columns = new List<DataSource.Column>();
        columns.add(DataSource.Column.text('orderId', 255)); // 注文ID
        columns.add(DataSource.Column.text('customerName', 255)); // 顧客名
        columns.add(DataSource.Column.url('displayUrl')); // 表示用URL(必須)
        columns.add(DataSource.Column.text('externalId', 255)); // 外部ID(必須)
        tables.add(DataSource.Table.get('orders', 'orderId', columns));
        schema.tables = tables;
        
        return schema;
    }
    
    // データ取得(クエリ)の処理
    // SOQL が実行されるとこのメソッドが呼び出される
    override public DataSource.TableResult query(DataSource.QueryContext context) {
        // ここに外部システムからデータを取得する実際のロジックを実装する
        // 例: HTTPコールアウトを行い、結果を解析する
        
        List<Map<String, Object>> data = new List<Map<String, Object>>();
        // ダミーデータを生成
        Map<String, Object> row1 = new Map<String, Object>();
        row1.put('orderId', '1001');
        row1.put('customerName', 'Taro Yamada');
        row1.put('displayUrl', '/1001');
        row1.put('externalId', 'ext-1001');
        data.add(row1);

        Map<String, Object> row2 = new Map<String, Object>();
        row2.put('orderId', '1002');
        row2.put('customerName', 'Hanako Suzuki');
        row2.put('displayUrl', '/1002');
        row2.put('externalId', 'ext-1002');
        data.add(row2);

        return DataSource.TableResult.get(true, null, context.tableSelection.table, data);
    }
    
    // 検索処理(今回は未実装)
    override public List<DataSource.TableResult> search(DataSource.SearchContext context) {
        return null;
    }
}

注:上記の query メソッド内は、説明を簡潔にするためのダミーデータ生成ロジックです。実際のプロジェクトでは、connectionInfo からエンドポイントURLや認証情報を取得し、Http クラスなどを用いて外部システムへの API コールアウトを実装します。


注意事项

External Objects は非常に強力な機能ですが、その特性を理解し、いくつかの注意点を考慮する必要があります。

権限とセキュリティ

  • External Object の参照・更新・削除権限は、通常のオブジェクトと同様にプロファイルや権限セットで制御します。
  • 外部システムへの認証は External Data Source レベルで設定されます。Named Credential (指定ログイン情報) を使用することで、エンドポイント URL や認証情報をコードから分離し、安全かつ宣言的に管理することが強く推奨されます。

API 制限とパフォーマンス

  • Salesforce Connect には、Salesforce の標準 API 制限とは別の、専用のコールアウト制限が存在します。大量のデータを扱う場合や、多くのユーザーが同時にアクセスする場合には、この制限を考慮する必要があります。
  • パフォーマンスは、外部システムの応答速度とネットワーク遅延に大きく依存します。外部システムの API が低速な場合、Salesforce 上でのレコード表示やレポート実行も遅くなります。外部システム側で、クエリが効率的に処理されるようにインデックスを最適化することが不可欠です。
  • 一度に大量のデータを取得しようとすると、タイムアウトやパフォーマンス低下の原因となります。SOQL で WHERE 句を適切に使用し、取得するデータ量を絞り込むことが重要です。

機能的な制約

  • サポートされない機能:External Objects は、Apex トリガー、ワークフロールール(一部のアクションを除く)、積み上げ集計項目など、データが Salesforce 内に存在することを前提とした多くの機能をサポートしていません。
  • * リレーション:External Object は、標準・カスタムオブジェクトへの外部参照関係と、他の External Object への間接参照関係をサポートします。これらのリレーションシップを正しく設計することが、データ統合の鍵となります。
  • 検索:デフォルトでは、External Object は Salesforce のグローバル検索の対象外です。検索可能にするには、カスタムアダプタで検索機能を実装し、同期時に設定を有効にする必要があります。

エラーハンドリング

外部システムがダウンしている、あるいはネットワークに問題がある場合、External Object へのアクセスは失敗します。ユーザーにはエラーメッセージが表示されます。外部システムの可用性と堅牢性を確保し、監視体制を整えることが、安定した運用には不可欠です。


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

External Objects は、データを Salesforce にコピーすることなく、外部システムとリアルタイムに連携するための優れたソリューションです。これにより、データの一元管理と鮮度を保ちながら、ストレージコストと開発・保守のオーバーヘッドを削減できます。

ベストプラクティス

  1. 適切なユースケースの選択:リアルタイム性が求められ、主に参照が中心となるデータ(例:注文履歴、請求情報)に最適です。頻繁な更新や、Salesforce の複雑な自動化(トリガーなど)が必要なデータは、引き続き ETL や API連携によるデータ同期が適している場合があります。
  2. 外部システムの最適化:パフォーマンスの鍵は外部システムが握っています。API の応答速度を改善し、効率的なクエリが実行できるよう、データベースのインデックス設計や API の実装を見直してください。
  3. 標準アダプタの優先:接続先が OData をサポートしている場合は、迷わず OData アダプタを使用してください。コーディングが不要で、保守性も高くなります。Apex Connector Framework は、それ以外のケースのための強力な選択肢です。
  4. Named Credential の活用:認証情報をハードコーディングせず、Named Credential を使用してセキュリティと管理性を向上させましょう。
  5. データ量の考慮:ページネーションを適切に処理し、ユーザーが一度に大量のデータをリクエストしないような UI/UX を設計することが重要です。レポートやリストビューでは、必ずフィルタ条件を設定するようユーザーをガイドしてください。

External Objects を正しく理解し、その特性を活かすことで、Salesforce をハブとした、よりシームレスで強力な統合エンタープライズアーキテクチャを実現することができます。

コメント