概要とビジネスシーン
Salesforce Connect は、外部システムに保存されたデータを Salesforce 内で直接、リアルタイムで表示および操作することを可能にする画期的な機能です。これにより、データが Salesforce に複製されることなく、常に最新の外部情報を参照できるため、データ同期の複雑さやストレージコストの問題を大幅に軽減します。
実際のビジネスシーン
シーンA:製造業 - ERP連携による在庫可視化
- ビジネス課題:製造業の営業担当者は、商談中に顧客からの製品納期問い合わせに対して、常に最新のERP(Enterprise Resource Planning)システムの在庫状況や生産計画を確認する必要がありました。しかし、ERPとSalesforceのデータ同期にはタイムラグがあり、古い情報に基づいた回答をしてしまうリスクがありました。
- ソリューション:Salesforce Connect を利用し、ERPの在庫情報をSalesforceの外部オブジェクトとしてリアルタイムで表示。営業担当者は商談レコードから直接、現在の在庫数や生産リードタイムを確認できるようになりました。
- 定量的効果:営業の応答時間が20%短縮され、顧客満足度が向上。誤った納期情報によるキャンセルが年間5%削減されました。
シーンB:サービス業 - 外部チケットシステムとの顧客情報統合
- ビジネス課題:顧客サポート部門では、Salesforce Service Cloud と外部の専門チケット管理システム(例:Jira Service Management)を併用しており、オペレーターが顧客対応時にSalesforceの顧客情報とJiraのチケット情報をそれぞれ別画面で確認する必要がありました。これにより、顧客の全体像を把握するのに時間がかかり、対応の質が低下していました。
- ソリューション:Salesforce Connect を活用してJiraのチケットデータを外部オブジェクトとしてService Cloudに統合。顧客のケースレコードから関連するJiraチケットを一覧表示し、詳細も確認できるようにしました。
- 定量的効果:オペレーターの顧客対応時間が平均15%短縮され、初回解決率(FCR)が10%向上しました。
シーンC:Eコマース - 外部注文履歴データベースとの統合
- ビジネス課題:Eコマース企業は、Salesforce Marketing Cloud や Sales Cloud で顧客管理を行っていますが、過去の膨大な注文履歴データは外部のデータウェアハウスに格納されており、Salesforceからは参照できませんでした。これにより、顧客の購買パターンに基づいたパーソナライズされた営業やマーケティング施策が打ちにくい状況でした。
- ソリューション:Salesforce Connect の Custom Adapter を利用してデータウェアハウスの注文履歴を外部オブジェクトとしてSalesforceに統合。顧客の360度ビューから直接、過去の購入履歴や頻度をリアルタイムで分析できるようになりました。
- 定量的効果:顧客へのパーソナライズされた推奨が容易になり、アップセル・クロスセルの機会が12%増加。データサイロが解消され、顧客データ分析の効率が25%向上しました。
技術原理とアーキテクチャ
Salesforce Connect の基礎的な動作メカニズムは、外部システムに保存されているデータをあたかも Salesforce 内の標準オブジェクトやカスタムオブジェクトのように扱うための「外部オブジェクト(External Object)」を提供することにあります。データは Salesforce に複製されず、ユーザーが外部オブジェクトにアクセスするたびに、Salesforce Connect はリアルタイムで外部データソースにクエリを発行し、結果を Salesforce UI に表示します。
主要なコンポーネントは以下の通りです。
- 外部データソース (External Data Source):外部システムへの接続情報(URL、認証情報、アダプタータイプなど)を定義します。
- 外部オブジェクト (External Object):外部データソース内の特定のテーブルやエンティティに対応し、Salesforceのオブジェクトのように振る舞います。標準/カスタムオブジェクトと同様に、タブ、リストビュー、詳細ページ、レポートなどで利用可能です。
- アダプター (Adapter):Salesforce と外部システム間の通信プロトコルを定義します。主なアダプタータイプには OData 2.0、OData 4.0、Salesforce Cross-Org (Salesforce to Salesforce)、そして Apex Custom Adapter があります。
- 外部 ID (External ID):外部システムにおけるレコードの一意の識別子で、外部オブジェクトの項目としてマッピングされます。
データフローは以下のようになります。
| ステップ | 説明 |
|---|---|
| 1. ユーザー操作 | Salesforce ユーザーが外部オブジェクトのリストビュー、詳細ページ、またはレポートにアクセスします。 |
| 2. クエリ発行 | Salesforce Connect は、外部データソース定義に基づいて、対応するアダプターを介して外部システムへリアルタイムなデータクエリ(例: OData クエリ、Apex Custom Adapter のメソッド呼び出し)を発行します。 |
| 3. 外部システム処理 | 外部システムはクエリを受け取り、該当するデータを検索・取得します。 |
| 4. データ返却 | 外部システムは取得したデータを Salesforce Connect に返します(例: OData JSONレスポンス、Apex Custom Adapter のデータ構造)。 |
| 5. Salesforce UI表示 | Salesforce Connect は受け取ったデータを Salesforce UI に変換し、ユーザーに表示します。 |
ソリューション比較と選定
Salesforce Connect は外部データ統合の強力なツールですが、常に最適なソリューションとは限りません。他の一般的な統合アプローチと比較し、その特性を理解することが重要です。
| ソリューション | 適用シーン | パフォーマンス | Governor Limits | 複雑度 |
|---|---|---|---|---|
| Salesforce Connect | リアルタイム性が最優先、データが非常に大量でSalesforceに複製したくない場合、外部システムが比較的安定している場合。 | 外部システムのAPI応答速度とネットワーク速度に依存。検索やソートは外部システムの性能に大きく影響される。 | Apex Custom Adapter を使う場合、Apexのコールアウト制限(例: 100回のコールアウト/トランザクション)が適用される。外部オブジェクトのレポート制限など。 | 設定は比較的容易(特にOData)。Custom Adapter はApex開発スキルが必要。 |
| ETLツール (MuleSoft, Informaticaなど) を介したデータ同期 | Salesforce内でのデータの永続化が必要、複雑なデータ変換・加工が必要、外部システムの可用性が低い場合でもSalesforce側でデータを見たい場合。 | ETLプロセス実行時のみ外部システムにアクセス。同期後のSalesforce内データアクセスは高速。 | Salesforce内でのDML操作やデータサイズに関するGovernor Limitsが適用される。 | ETLツールの学習と設定、マッピング、スケジューリングが必要。 |
| Apex Callout + Custom UI (Visualforce/LWC) | 非常に特定のビジネスロジックやUI要件がある場合、外部システムとの密結合が必要な場合、Salesforce Connect の標準機能で実現できない複雑なインタラクション。 | Apexコードと外部APIの効率性に依存。カスタムUIのレンダリング速度も影響。 | Apexのコールアウト制限(100回/トランザクション、1日250,000回非同期)が直接適用される。CPU時間、ヒープサイズなど。 | Apex開発、Visualforce/LWC開発、外部API連携の深い知識が必要。 |
Salesforce Connect を使用すべき場合:
- ✅ リアルタイム性が最優先され、常に最新の外部データを参照する必要がある。
- ✅ 外部データが膨大であり、Salesforce のストレージ容量を圧迫したくない、またはデータの同期に多大なリソースをかけたくない。
- ✅ 外部システムが標準的な API (OData など) を提供しており、比較的シンプルなデータモデルを Salesforce 内で表示したい。
- ✅ Salesforce の標準機能(リストビュー、詳細ページ、関連リスト、レポート)で外部データを表示・利用したい。
- ❌ 不適用シーン:外部システムが頻繁にダウンする、APIの応答速度が極めて遅い、非常に複雑なデータ変換がSalesforce側で必要、厳密な全文検索性能が求められる場合(外部システムの検索性能に依存するため)。
実装例
ここでは、Salesforce Connect の最も柔軟なオプションである Custom Adapter を利用して、外部データソースからデータを取得する基本的な Apex コードの例を示します。この例では、架空の外部システムから注文(Order)データを取得することを想定しています。
1. Custom Adapter クラスの作成
public class ExternalOrderAdapter implements DataSource.Connection {
// 外部オブジェクトのスキーマ情報を返す
public DataSource.Schema getSchema() {
// ExternalObject を定義し、外部システムのテーブル名とラベルを設定
DataSource.Table orderTable = DataSource.Table.builder('External_Order__x') // 外部オブジェクトのAPI参照名
.label('External Order') // UI表示ラベル
.pluralLabel('External Orders')
.custom(true) // カスタムオブジェクトとして扱う
.queryable(true) // クエリ可能
.retrievable(true) // IDで取得可能
.searchable(true) // 検索可能
.createable(false) // 作成不可 (リードオンリーの場合)
.updateable(false) // 更新不可
.deleteable(false) // 削除不可
.build();
// 外部オブジェクトのフィールドを定義
// Id フィールドは常に必要 (外部システムのユニークIDとマッピング)
orderTable.fields(new List<DataSource.Column>{
DataSource.Column.builder('Id', DataSource.ColumnType.Text)
.length(255)
.label('Order ID')
.externalId(true) // 外部システムの一意IDであることを示す
.isUnique(true)
.build(),
DataSource.Column.builder('OrderNumber__c', DataSource.ColumnType.Text)
.length(50)
.label('Order Number')
.build(),
DataSource.Column.builder('OrderDate__c', DataSource.ColumnType.DateTime)
.label('Order Date')
.build(),
DataSource.Column.builder('TotalAmount__c', DataSource.ColumnType.Double)
.label('Total Amount')
.scale(2)
.build(),
DataSource.Column.builder('CustomerEmail__c', DataSource.ColumnType.Text)
.length(255)
.label('Customer Email')
.filterable(true) // フィルタリング可能にする
.build()
});
// 複数のテーブルを持つことも可能
return DataSource.Schema.builder().addTable(orderTable).build();
}
// 外部オブジェクトからデータをクエリする (リストビュー、レポートなど)
public DataSource.QueryResult query(DataSource.QueryContext context) {
System.debug('Querying External Orders with context: ' + context);
// 外部API呼び出しのモックアップ
// 実際にはここで外部システムへのコールアウトを行い、フィルタリングやソートを適用
List<Map<String, Object>> externalData = new List<Map<String, Object>>();
for (Integer i = 1; i <= 3; i++) {
Map<String, Object> row = new Map<String, Object>();
row.put('Id', 'EXO-000' + i);
row.put('OrderNumber__c', 'PO-' + System.now().format('yyyyMMdd') + '-' + i);
row.put('OrderDate__c', System.now().addHours(-i));
row.put('TotalAmount__c', 100.00 * i);
row.put('CustomerEmail__c', 'customer' + i + '@example.com');
externalData.add(row);
}
// context.filters や context.orderBy を処理し、外部システムへのリクエストに含める
// 例: context.filters は where 句の条件を表す
if (context.filters != null && !context.filters.isEmpty()) {
List<Map<String, Object>> filteredData = new List<Map<String, Object>>();
for (Map<String, Object> row : externalData) {
Boolean matches = true;
for (DataSource.Filter filter : context.filters) {
if (filter.field.equals('CustomerEmail__c') && filter.operator == DataSource.Filter.Operator.EQ) {
if (row.get('CustomerEmail__c') != filter.value) {
matches = false;
break;
}
}
// 他のフィルタリングロジックをここに追加
}
if (matches) {
filteredData.add(row);
}
}
externalData = filteredData;
}
// context.limit や context.offset を適用してページネーションを実装
// ...
// Salesforce Connect にデータを返却
return DataSource.QueryResult.builder()
.rows(externalData)
.has // 全件数が不明な場合は null を返す
.build();
}
// 特定の外部オブジェクトレコードを ID で取得する (詳細ページなど)
public DataSource.RetrieveResult retrieve(DataSource.RetrieveContext context) {
System.debug('Retrieving External Order with context: ' + context);
// 外部API呼び出しのモックアップ
// 実際にはここで外部システムへのコールアウトを行い、特定のIDのデータを取得
Map<String, Object> row = new Map<String, Object>();
row.put('Id', context.ids[0]); // RetrieveContext は常に単一のIDで呼び出される
row.put('OrderNumber__c', 'PO-RETRIEVE-' + context.ids[0]);
row.put('OrderDate__c', System.now());
row.put('TotalAmount__c', 999.99);
row.put('CustomerEmail__c', 'retrieved_customer@example.com');
return DataSource.RetrieveResult.builder()
.row(row)
.build();
}
// 外部システムで検索を行う (グローバル検索など)
public DataSource.SearchResult search(DataSource.SearchContext context) {
// 必要に応じて実装。検索機能が不要な場合は空のリストを返す
System.debug('Searching External Orders with context: ' + context);
return DataSource.SearchResult.builder().rows(new List<Map<String, Object>>()).build();
}
// 外部システムのデータを作成する (ReadOnlyの場合は不要)
public DataSource.CreateResult create(DataSource.CreateContext context) {
throw new DataSource.ReadOnlyException('Create operation not supported for ExternalOrderAdapter.');
}
// 外部システムのデータを更新する (ReadOnlyの場合は不要)
public DataSource.UpdateResult update(DataSource.UpdateContext context) {
throw new DataSource.ReadOnlyException('Update operation not supported for ExternalOrderAdapter.');
}
// 外部システムのデータを削除する (ReadOnlyの場合は不要)
public DataSource.DeleteResult delete(DataSource.DeleteContext context) {
throw new DataSource.ReadOnlyException('Delete operation not supported for ExternalOrderAdapter.');
}
}
2. 外部データソースの設定
- Salesforce の設定 (Setup) から「外部データソース」を検索して選択します。
- 「新規外部データソース」をクリックします。
- タイプとして「Apex」を選択し、先ほど作成した
ExternalOrderAdapterクラスを指定します。 - 保存後、「検証して同期」をクリックし、外部オブジェクト
External_Order__xを作成します。
これで、Salesforce Connect Custom Adapter を介して外部システム(この例ではモックデータ)のデータが Salesforce 内で外部オブジェクトとして利用可能になります。実際のプロジェクトでは、query や retrieve メソッド内で、HTTPコールアウトを介して実際の外部 API を呼び出し、レスポンスを解析してデータ構造にマッピングする必要があります。
注意事項とベストプラクティス
Salesforce Connect を効果的に利用するためには、いくつかの重要な注意事項とベストプラクティスがあります。
権限要件
- プロファイル/権限セット:ユーザーが外部オブジェクトにアクセスするには、そのプロファイルまたは権限セットで「外部データソースへのアクセスを許可(Allow Connect to External Data Sources)」権限が付与されている必要があります。
- 外部オブジェクトの参照権限:個々の外部オブジェクトに対しても、通常のオブジェクトと同様に「参照(Read)」などの権限が必要です。
- 外部システム認証:外部データソースの認証情報(例: 名前付き資格情報、OAuth)にアクセスできる権限も考慮する必要があります。
Governor Limits
Salesforce Connect 自体に直接的な Governor Limits は少ないですが、使用するアダプターや関連機能には以下の制限が適用されます。
- Apex Custom Adapter の場合:
- コールアウトの制限:同期コールアウトはトランザクションあたり最大 100 回、合計 120 秒。非同期コールアウトは1日あたり最大 250,000 回の制限があります。外部システムへの各リクエストはこれに計上されます。
- CPU時間、ヒープサイズ:通常の Apex Governor Limits が適用されます。外部システムからの大量データの処理には注意が必要です。
- 外部オブジェクトのレポート:外部オブジェクトを含むレポートは、最大 20,000 件のレコードしか返しません。
- 検索結果:外部オブジェクトの検索は、外部システムの検索性能と制限に依存します。
エラー処理
- 外部システムの応答:外部システムからのタイムアウト、認証エラー、データ不整合などのエラーを Apex Custom Adapter で適切にハンドリングし、Salesforce Connect の例外クラス(例:
DataSource.Connection.DataSourceException)を使用して Salesforce 側にエラーメッセージを返すようにします。 - ロギング:Apex Custom Adapter 内で詳細なデバッグログを実装し、外部システムへのリクエストとレスポンス、エラー発生時のスタックトレースなどを記録することで、問題発生時のトラブルシューティングを容易にします。
- タイムアウト設定:外部データソースの設定でコールアウトタイムアウトを設定し、応答が遅い外部システムからの影響を最小限に抑えます。
パフォーマンス最適化
- 外部システム側の最適化:Salesforce Connect のパフォーマンスは外部システムの API 応答速度に直接依存します。外部システムのデータベースインデックス、APIのキャッシュ、ネットワーク帯域を最適化することが最優先です。
- クエリの最適化 (ODataの場合):
$selectを使用して必要な項目のみを取得します。$filter、$orderby、$top、$skipを活用し、外部システム側でデータフィルタリング、ソート、ページネーションを実行させます。
- Custom Adapter での効率的なデータ処理:
queryメソッド内で、QueryContextのフィルタリング(context.filters)やソート(context.orderBy)の情報を外部 API リクエストに正確にマッピングし、外部システム側でフィルタリング・ソートを行うようにします。- 不必要なデータを取得しないよう、APIレスポンスのパース処理を効率化します。
- リレーションシップの適切化:外部オブジェクトと標準/カスタムオブジェクト間のリレーションシップ(ルックアップまたは間接ルックアップ)を設計する際、関連レコードの取得方法がパフォーマンスに与える影響を考慮します。特に間接ルックアップは、外部IDに基づく効率的なマッチングを提供します。
- キャッシュ戦略の検討:一部の静的または変更頻度の低い外部データに対しては、Salesforce Connect を使用せず、ETLツールなどでSalesforceに同期し、キャッシュする戦略も考慮できます。ただし、リアルタイム性が要求される場合はこの選択肢は避けるべきです。
よくある質問 FAQ
Q1:Salesforce Connect と従来の ETL ツールによるデータ同期はどのように使い分けるべきですか?
A1:Salesforce Connect は、データが常に外部システムで管理され、Salesforce 内でリアルタイムに参照・表示したい場合に最適です。データ同期は、Salesforce 内でデータを永続化し、オフラインアクセス、複雑なデータ変換、または外部システムの可用性に依存しない分析を行いたい場合に適しています。
Q2:外部オブジェクトの検索やリストビューのパフォーマンスが悪い場合、どのようにデバッグすればよいですか?
A2:まず、外部システムの API ログとパフォーマンスメトリクスを確認してください。Salesforce 側では、デバッグログを有効にし、Apex Custom Adapter のコールアウト詳細や OData クエリの生成内容を検証します。特に OData の場合、発行されたクエリ文字列(例: $filter や $orderby が正しく生成されているか)が外部システムで効率的に処理されているかを確認することが重要です。
Q3:外部オブジェクトで Salesforce のレポートやダッシュボードを作成する際の注意点はありますか?
A3:はい、可能です。ただし、外部オブジェクトを含むレポートは最大 20,000 レコードまでしか返しません。また、外部オブジェクト間のリレーションシップや、外部オブジェクトと標準/カスタムオブジェクト間のリレーションシップが正しく設定されている必要があります。レポートの結合タイプによってはパフォーマンスが低下する可能性があるため、テストと最適化が必要です。
まとめと参考資料
Salesforce Connect は、現代の企業が直面するデータサイロの課題を解決し、外部データを Salesforce 環境にシームレスに統合するための強力なソリューションです。リアルタイム性とストレージ効率の高さは、特にインテグレーションエンジニアにとって大きな魅力となるでしょう。OData や Cross-Org アダプターは設定が比較的容易であり、Apex Custom Adapter は複雑な外部システムや特定のビジネスロジックに対応する究極の柔軟性を提供します。本記事で解説した技術原理、実装例、そしてベストプラクティスを理解することで、貴社の Salesforce 環境におけるデータ統合戦略を次のレベルへと引き上げることが可能です。
公式リソース:
- 📖 公式ドキュメント:Salesforce Connect の概要
- 📖 公式ドキュメント:Custom Adapters for Salesforce Connect
- 🎓 Trailhead モジュール:Salesforce Connect の基礎
- 🔧 関連 GitHub サンプル:Salesforce Connect の Apex カスタムアダプターの例
- https://github.com/forcedotcom/Salesforce-Connect-Custom-Adapter-for-File-System (これはファイルシステム用ですが、実装の参考になります)
コメント
コメントを投稿