Salesforce 外部对象:实现无缝数据整合的深度解析
背景与应用场景
在现代企业架构中,数据往往分散在不同的系统中。销售团队在 Salesforce 中管理客户关系,而订单、发货和库存信息可能存放在企业资源规划(ERP)系统中,产品详细信息则可能由另一个产品信息管理(PIM)系统维护。这种数据孤岛(Data Silos)现象导致了信息的不一致和工作效率的低下。传统的解决方案通常是采用 ETL (Extract, Transform, Load) 工具将数据从外部系统定期同步或复制到 Salesforce 中。然而,这种方式会带来数据延迟、存储成本增加以及维护复杂性等问题。
为了解决这一挑战,Salesforce 推出了 Salesforce Connect,其核心组件就是 External Objects (外部对象)。External Objects 是一种特殊的 Salesforce 对象,它的数据并不实际存储在 Salesforce 平台,而是实时地存放在外部系统中。通过 External Objects,用户可以在 Salesforce 界面中无缝地查看、搜索甚至修改外部系统的数据,就像操作标准的 Salesforce 自定义对象一样,从而实现了数据的“虚拟化整合”。
典型的应用场景包括:
- 订单历史查看: 在客户(Account)页面上,直接展示该客户存储在 ERP 系统中的所有历史订单和发货状态,无需将海量订单数据复制到 Salesforce。 - 集中的产品库: 当产品信息由专门的系统管理时,销售人员可以在创建机会(Opportunity)时,直接查询和关联外部产品库中的产品,确保数据源的唯一和准确。
- 金融数据展示: 在不牺牲安全性的前提下,向财务或高管团队展示存储在外部财务系统中的客户信用评级或支付记录。
原理说明
External Objects 的实现依赖于 Salesforce Connect 框架。其工作原理可以概括为以下几个关键步骤:
1. 定义外部数据源 (External Data Source)
首先,管理员需要在 Salesforce 中配置一个 External Data Source (外部数据源),它定义了 Salesforce 如何连接到外部系统。Salesforce Connect 支持多种适配器(Adapter)协议:
- OData 2.0/4.0: OData (开放数据协议) 是一种基于 REST 的标准化协议,是连接外部系统的首选方式。如果外部系统提供了 OData 端点,配置会非常简单。
- Cross-Org: 用于连接另一个 Salesforce Org,实现跨组织的数据访问。
- Custom (Apex Connector Framework): 如果外部系统没有提供 OData 接口,但提供了 REST 或 SOAP API,开发者可以使用 Apex Connector Framework 编写自定义的 Apex 代码作为适配器,将外部 API 的请求和响应格式翻译成 Salesforce Connect 可以理解的格式。
在配置外部数据源时,还需要定义认证方式,如匿名访问、密码认证或 OAuth 2.0。
2. 创建外部对象 (External Objects)
当外部数据源连接成功后,Salesforce 会根据外部系统的数据结构(例如 OData 服务中的实体),让管理员选择并创建对应的 External Objects。这个过程类似于根据数据库表创建自定义对象。Salesforce 会为外部表的每个字段创建对应的外部对象字段。此时,Salesforce 仅保存了外部对象的元数据(Metadata),即对象的定义、字段列表和 API 名称等,而不存储任何实际的记录数据。
3. 实时数据交互
当用户通过列表视图、页面布局、报表或 SOQL 查询访问 External Object 时,Salesforce 会在后台执行以下操作:
- Salesforce Connect 将用户的请求(如 "SELECT Id, Name FROM Orders__x")实时翻译成外部系统能够理解的查询语言(如一个 OData GET 请求)。
- 该请求通过外部数据源的配置被发送到外部系统。
- 外部系统处理请求并返回数据。
- Salesforce Connect 接收到返回的数据,并将其呈现在 Salesforce UI 或 API 响应中。
整个过程是实时的,确保了用户看到的数据永远是最新的。
4. 关系建立 (Relationships)
External Objects 同样支持关系查询,这是实现无缝体验的关键。Salesforce 提供了三种查找关系来连接不同对象:
- External Lookups (外部查找): 用于连接两个都来自同一个外部数据源的外部对象,或者将一个子对象(标准、自定义或外部)关联到一个父外部对象。父外部对象的 ID 被用作关联键。 - Indirect Lookups (间接查找): 用于将一个子对象(标准或自定义)关联到一个父外部对象。它使用子对象上的一个自定义字段(标记为 External ID)去匹配父外部对象上的一个唯一标识字段,这个唯一标识字段的值并非 Salesforce Record ID。这种关系非常适用于将 Salesforce 内部数据(如 Account)与外部系统的记录(如 ERP 中的客户)关联起来。
示例代码
当外部系统不支持 OData 协议时,我们可以使用 Apex 自定义适配器。以下代码示例展示了如何通过实现 `DataSource.Provider` 和 `DataSource.Connection` 接口来创建一个简单的自定义连接器,该连接器从一个虚构的数据源获取数据。
注意: 这是一个结构性示例,旨在说明 Apex Connector Framework 的工作方式。实际实现需要根据外部系统的 API 进行调整。
1. 创建 Provider 类
Provider 类用于声明数据源的功能和认证方式。
// MyDataSourceProvider.cls // 这个类定义了数据源的提供者,声明了其支持的认证能力和数据操作类型。 public class MyDataSourceProvider extends DataSource.Provider { // getAuthenticationCapabilities() 方法返回数据源支持的认证类型。 // 这里我们声明它支持基本的用户名和密码认证 (Password)。 public override List<DataSource.AuthenticationCapability> getAuthenticationCapabilities() { List<DataSource.AuthenticationCapability> capabilities = new List<DataSource.AuthenticationCapability>(); capabilities.add(DataSource.AuthenticationCapability.PASSWORD); return capabilities; } // getCapabilities() 方法返回数据源支持的操作类型。 // 这里我们声明它支持基本的查询 (ROW_QUERY) 和搜索 (SEARCH)。 public override List<DataSource.Capability> getCapabilities() { List<DataSource.Capability> capabilities = new List<DataSource.Capability>(); capabilities.add(DataSource.Capability.ROW_QUERY); capabilities.add(DataSource.Capability.SEARCH); return capabilities; } // getConnection() 方法在需要与外部系统交互时被调用,它返回一个 Connection 实例。 // 这个实例将负责处理实际的数据查询逻辑。 public override DataSource.Connection getConnection(DataSource.ConnectionParams connectionParams) { return new MyDataSourceConnection(connectionParams); } }
2. 创建 Connection 类
Connection 类负责处理实际的数据查询、搜索和同步逻辑。
// MyDataSourceConnection.cls // 这个类实现了与外部系统的实际连接和数据交互逻辑。 public class MyDataSourceConnection extends DataSource.Connection { private DataSource.ConnectionParams connectionInfo; // 构造函数,保存连接参数,其中可能包含认证信息。 public MyDataSourceConnection(DataSource.ConnectionParams connectionParams) { this.connectionInfo = connectionParams; } // sync() 方法用于发现外部系统中的表(Tables)和列(Columns), // 以便在 Salesforce 中创建对应的外部对象和字段。 public override List<DataSource.Table> sync() { // 在实际场景中,这里会调用外部系统的元数据API来获取表结构。 // 以下为硬编码示例。 List<DataSource.Table> tables = new List<DataSource.Table>(); List<DataSource.Column> columns = new List<DataSource.Column>(); columns.add(DataSource.Column.text('ID', 255)); // 外部ID columns.add(DataSource.Column.text('Name', 255)); // 名称 columns.add(DataSource.Column.text('Status', 50)); // 状态 // 定义一个名为 "Orders" 的表,它将映射为一个外部对象。 tables.add(DataSource.Table.create('Orders', 'Name', columns)); return tables; } // query() 方法是核心,负责处理来自 Salesforce 的 SOQL 查询。 // 它将 SOQL 查询转换为对外部系统的 API 调用,并返回结果。 public override DataSource.TableResult query(DataSource.QueryContext context) { // 在实际场景中,这里会解析 context 对象(包含查询的表、列、过滤条件等), // 然后构造一个对外部系统的 REST 或 SOAP API 调用。 // 为了简化,我们直接返回硬编码的数据。 List<Map<String, Object>> responseData = new List<Map<String, Object>>(); Map<String, Object> row1 = new Map<String, Object>(); row1.put('ID', '1001'); row1.put('Name', 'Order_Alpha'); row1.put('Status', 'Shipped'); responseData.add(row1); Map<String, Object> row2 = new Map<String, Object>(); row2.put('ID', '1002'); row2.put('Name', 'Order_Beta'); row2.put('Status', 'Processing'); responseData.add(row2); // 使用 DataSource.TableResult.success 将外部数据包装成 Salesforce 可识别的格式。 return DataSource.TableResult.success(responseData); } // search() 方法负责处理 SOSL 搜索请求。 public override List<DataSource.TableResult> search(DataSource.SearchContext context) { // 实现搜索逻辑,调用外部系统的搜索API。 // 此处省略具体实现。 return new List<DataSource.TableResult>(); } }
注意事项
虽然 External Objects 功能强大,但在使用时必须考虑以下几点:
1. 权限与安全
- Salesforce 端:External Objects 遵循 Salesforce 的标准安全模型。你可以通过简档(Profile)和权限集(Permission Set)来控制用户对外部对象及其字段的访问权限(创建、读取、更新、删除)。
- 外部数据源端:在配置 External Data Source 时设置的认证凭证决定了 Salesforce 以哪个用户的身份去访问外部系统。所有 Salesforce 用户都将共享这一凭证,因此需要确保该凭证在外部系统中的权限是最小必要原则。
2. API 限制与性能
- Salesforce Connect 调用限制:Salesforce Connect 有其自身的 API 调用限制,它独立于通用的 Salesforce API 限制。请在实施前查阅最新的官方文档以了解具体的限制数量。
- 外部系统性能:External Objects 的性能完全取决于外部系统的响应速度。如果外部系统的 API 响应缓慢或不稳定,那么在 Salesforce 中访问这些外部对象也会同样缓慢,严重影响用户体验。
- 高数据量:在处理大量数据时,必须确保外部系统支持高效的分页(Paging)和过滤。对于 OData 适配器,建议使用服务器驱动分页(Server-driven Paging)以获得最佳性能。
3. 功能限制
External Objects 并不支持所有标准或自定义对象的功能,主要限制包括:
- 不支持 Apex Triggers、流程(Flows)和验证规则(Validation Rules)。
- 报表和仪表板功能受限,例如不支持趋势分析和某些图表类型。
- 不支持公式字段中引用其他对象的字段(跨对象公式)。
- 在搜索方面,默认行为可能不会立即返回所有结果,需要进行特定配置。
4. 错误处理
如果外部系统宕机、网络中断或返回错误,Salesforce 中的用户在访问外部对象时会直接看到错误信息。因此,保证外部系统的健壮性和高可用性至关重要。
总结与最佳实践
External Objects 是 Salesforce 平台上一项强大的数据集成工具,它通过数据虚拟化的方式,为用户提供了访问外部系统数据的实时、无缝体验,尤其适用于以下场景:
- 你需要实时访问外部数据,并且数据延迟是不可接受的。
- 外部数据量非常庞大,将其全部复制到 Salesforce 既不经济也不高效。
- 你希望将外部数据作为 Salesforce 业务流程的一部分,但数据的“事实来源(Source of Truth)”仍在外部系统中。
最佳实践建议:
- 优先选择 OData:如果外部系统支持 OData,应优先使用 OData 适配器。这是最简单、最标准化的连接方式,可以减少大量的自定义开发工作。
- 优化外部系统性能:在将外部数据源暴露给 Salesforce 之前,务必对其进行性能测试和优化,特别是查询和分页功能。
- 理解并接受其局限性:在方案设计阶段,要充分评估 External Objects 的功能限制,确保它能满足业务需求。如果需要复杂的业务逻辑(如触发器、复杂验证),传统的 ETL 或 API 集成方案可能更为合适。
- 谨慎设计关系:合理利用 External Lookups 和 Indirect Lookups 来建立数据模型,确保 Salesforce 内部数据和外部数据能够正确地关联起来。
- 从小处着手:先从一个简单的、只读的外部对象开始,验证整个连接和数据访问流程,然后再逐步扩展到更复杂的、可写或包含关系的场景。
总而言之,Salesforce External Objects 并非要取代所有的传统数据集成模式,而是提供了一种轻量级、实时化的新选择。作为一名技术架构师,深刻理解其工作原理、适用场景和限制,将帮助你设计出更高效、更灵活、更具成本效益的集成解决方案。
评论
发表评论