深入解析 Salesforce 外部对象:一位架构师的 Salesforce Connect 数据虚拟化指南

背景与应用场景

作为一名 Salesforce 架构师,我在设计企业级解决方案时面临的核心挑战之一便是如何优雅地处理分散在不同系统中的数据。现代企业的数据版图往往是复杂的,核心客户数据可能存放在 Salesforce 中,而订单历史、发货状态、产品库存等关键信息则可能驻留在 ERP 系统(如 SAP 或 Oracle)、内部数据库、甚至其他云服务中。传统的数据集成方式,如 ETL (Extract, Transform, Load),虽然功能强大,但其固有的“数据复制”模式带来了诸多问题:数据延迟、存储成本增加、维护复杂性高,以及潜在的数据一致性风险。

在这样的背景下,Data Virtualization (数据虚拟化) 成为了一种极具吸引力的架构模式。其核心思想是“按需访问,而非复制存储”。Salesforce 通过其强大的附加产品 Salesforce Connect (Salesforce 连接),为我们提供了实现数据虚拟化的原生工具——External Objects (外部对象)

外部对象允许我们在 Salesforce 平台内,以一种几乎与标准或自定义对象无异的方式,实时地查看、搜索甚至修改存储在外部系统中的数据,而无需将这些数据实际复制到 Salesforce 的数据库中。这种能力为我们开辟了全新的解决方案可能性:

  • 360度客户视图增强:销售代表在客户记录页面上,可以直接看到来自 ERP 系统的实时订单历史和支付状态,而无需切换系统或等待夜间同步。
  • 实时供应链协同:在处理一个商机 (Opportunity) 时,可以实时查询外部库存系统,确保产品可用性,从而提供更准确的报价和交付预期。
  • 合规与数据驻留:对于有严格数据驻留要求的行业(如金融、医疗),可以将敏感数据保留在本地(On-premise)的数据库中,同时通过外部对象在 Salesforce 中安全地访问这些数据,满足合规要求。
  • 简化集成架构:对于只需要读取或简单更新的场景,使用外部对象可以避免构建复杂的、基于消息队列或批量处理的 ETL 流程,从而大大降低开发和维护成本。

从架构师的角度看,外部对象并非要取代所有传统集成方式,而是为我们的工具箱增添了一件利器。理解其工作原理、适用场景和局限性,是设计出高效、可扩展且成本优化的 Salesforce 解决方案的关键。


原理说明

Salesforce 外部对象的核心机制是 Salesforce Connect。它充当了一个强大的代理或翻译器,将 Salesforce 内部的操作(如 SOQL 查询、记录视图、全局搜索)实时转换为对外部系统的 API 调用。

整个工作流程可以分解为以下几个关键组件:

1. 外部数据源 (External Data Source)

这是集成的起点。您需要在 Salesforce 中创建一个外部数据源的配置记录,它定义了如何连接到外部系统。Salesforce Connect 提供了多种适配器 (Adapters) 类型来满足不同的集成需求:

  • OData 2.0 / OData 4.0:OData (Open Data Protocol, 开放数据协议) 是一种基于 REST 的、用于构建和消费数据 API 的 OASIS 标准。如果您的外部系统(如 SAP, SharePoint, 或许多现代 ERP)支持 OData,这是最推荐、最便捷的连接方式。Salesforce Connect 会自动解析 OData 服务元数据,让您可以轻松地将外部数据表“同步”为 Salesforce 外部对象。
  • Cross-Org:这个适配器专门用于连接另一个 Salesforce 组织。它利用 Salesforce REST API,让您可以在一个组织中无缝地访问另一个组织的数据。这对于大型企业中拥有多个 Salesforce 实例的场景非常有用。
  • Custom (Apex):当外部系统不提供 OData 接口,或者有非常特殊的认证、查询逻辑时,Apex 自定义适配器 (Apex Custom Adapter) 就派上了用场。这允许开发人员使用 Apex 代码编写自己的连接逻辑,通过实现 `DataSource.Provider` 和 `DataSource.Connection` 接口,将任何可以通过 Apex 调用的 API(REST, SOAP等)接入 Salesforce Connect 框架。这为集成提供了无限的可能性。

2. 外部对象 (External Object)

在配置好外部数据源后,您可以“同步 (Sync)”外部数据源。Salesforce 会根据外部系统的元数据(例如 OData 服务中的实体),在 Salesforce 中创建对应的外部对象定义。这个外部对象在 Salesforce 中拥有一个 API 名称(以 `__x` 结尾),可以像自定义对象一样添加自定义字段(仅限公式、查找等非存储类型)、设置页面布局、定义关系(如下文所述)等。但请记住,它的数据记录本身并不存储在 Salesforce。

3. 实时数据交互

当用户或自动化流程与外部对象交互时,魔法就发生了:

  • 查询 (Query):当您运行一个包含外部对象的 SOQL 查询或打开一个列表视图时,Salesforce Connect 会将该查询转换为对外部数据源的相应 API 调用。例如,一个 SOQL `SELECT Id, Name FROM Orders__x WHERE CustomerId__c = '001...'` 可能会被翻译成一个 OData 查询,如 `.../Orders?$filter=CustomerId eq '001...'&$select=Id,Name`。
  • 查看 (View):当您打开一个外部对象记录的详细页面时,Salesforce Connect 会根据记录 ID 向外部系统发起一个“按主键查找”的请求。
  • 写入 (Write):如果外部数据源被配置为可写入 (Writable),那么当用户在 Salesforce 中创建、编辑或删除外部对象记录时,Salesforce Connect 会相应地执行 HTTP POST, PATCH, 或 DELETE 请求到外部系统。

4. 关系 (Relationships)

外部对象可以参与 Salesforce 的数据模型,建立关系:

  • 查找关系 (Lookup Relationships):可以从标准、自定义或外部对象查找到外部对象。
  • 间接查找关系 (Indirect Lookup Relationships):这是一种特殊而强大的关系,它允许您从 Salesforce 对象(如 Account)查找到外部对象(如 Orders__x)。它通过将 Salesforce 上的一个唯一外部 ID 字段与外部对象上的某个字段进行匹配来建立连接。例如,可以将 Account 上的 `Legacy_ERP_Id__c` 字段与 Orders__x 上的 `CustomerId` 字段关联起来。
  • 外部查找关系 (External Lookup Relationships):用于连接两个相关的外部对象,前提是它们共享同一个外部数据源。

通过这套机制,Salesforce 将外部数据无缝地融入了其平台,为用户提供了统一的体验,同时保持了数据源的独立性和权威性,这正是数据虚拟化架构的精髓所在。


示例代码

对于架构师来说,理解最灵活的集成方式——Apex 自定义适配器——至关重要。以下示例代码来自 Salesforce 官方文档,展示了一个简单的自定义适配器的基本结构。它演示了如何连接到一个虚构的数据源并返回静态数据,这为连接到任何 REST 或 SOAP API 奠定了基础。

这个适配器包含两个核心类:`Provider` 类用于声明适配器的能力和获取连接实例,`Connection` 类则负责实现具体的数据操作逻辑。

/*
 *  Provider 类:定义数据源提供者
 *  这个类是 Salesforce Connect 框架的入口点,用于声明适配器的特性。
 */
global class MyCustomAdapterProvider extends DataSource.Provider {

    // 声明适配器支持的能力。例如,这里声明了支持基本的查询 (ROW_QUERY) 和搜索 (SEARCH)。
    // 其他能力还包括 DataSource.Capability.ROW_CREATE, ROW_UPDATE, ROW_DELETE 等。
    override global 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;
    }

    // 当 Salesforce 需要与外部数据源交互时,会调用此方法来获取一个 Connection 实例。
    // authenticationInfo 参数包含了在外部数据源设置中定义的所有认证信息。
    override global DataSource.Connection getConnection(
        DataSource.ConnectionParams connectionParams) {
        return new MyCustomAdapterConnection(connectionParams);
    }
}

/*
 *  Connection 类:实现数据交互逻辑
 *  这个类包含了与外部系统进行实际通信的核心代码。
 */
global class MyCustomAdapterConnection extends DataSource.Connection {

    private DataSource.ConnectionParams connectionParams;

    // 构造函数,保存连接参数
    global MyCustomAdapterConnection(
        DataSource.ConnectionParams connectionParams) {
        this.connectionParams = connectionParams;
    }

    // 实现 query 方法,这是处理 SOQL 查询的核心。
    // 当用户或系统查询此适配器下的外部对象时,此方法被调用。
    override global DataSource.Queryresult query(DataSource.QueryContext context) {
        // 在实际项目中,这里会构建一个 HTTP 请求到外部系统的 API
        // 然后解析返回的 JSON 或 XML,并将其构造成一个 DataSource.Table 对象。
        
        // 为了演示,我们创建一个静态的表格和数据。
        DataSource.Table table = getTableForDemo(context.tableSelection.columns);
        return DataSource.QueryResult.newResult(table);
    }

    // 实现 search 方法,处理全局搜索。
    override global List<DataSource.searchResult> search(
        DataSource.SearchContext context) {
        // 搜索逻辑与查询类似,但通常会查询多个外部对象(表)。
        // 实际场景中,会调用外部系统的搜索 API。
        List<DataSource.SearchResult> searchResults =
            new List<DataSource.SearchResult>();
        
        // 示例:返回一些静态搜索结果
        // ... (此处省略具体实现)
        
        return searchResults;
    }
    
    // 辅助方法:创建一个用于演示的静态数据表
    private DataSource.Table getTableForDemo(List<DataSource.Column> columns) {
        DataSource.Table table = new DataSource.Table();
        // 添加行数据
        DataSource.Row row1 = table.addRow();
        row1.addCell('ExternalId', 'ID1'); // 必须包含外部ID列
        row1.addCell('DisplayUrl', '/ID1'); // URL用于在Salesforce中点击
        row1.addCell('Name', 'Sample Record 1');
        
        DataSource.Row row2 = table.addRow();
        row2.addCell('ExternalId', 'ID2');
        row2.addCell('DisplayUrl', '/ID2');
        row2.addCell('Name', 'Sample Record 2');

        return table;
    }

    // 如果要支持写入操作,还需要实现 upsertRows, deleteRows 等方法。
    // override global List<DataSource.UpsertResult> upsertRows(DataSource.UpsertContext context) { ... }
    // override global List<DataSource.DeleteResult> deleteRows(DataSource.DeleteContext context) { ... }
}

⚠️ 这段代码旨在说明 Apex 自定义适配器的结构。实际项目中,`query` 和 `search` 方法内部会包含使用 `Http` 类的复杂逻辑,用于进行 API 调用、处理认证、错误捕获和数据解析。


注意事项

作为架构师,在方案中引入外部对象前,必须仔细评估以下限制和考量点:

1. 性能和延迟

这是首要考虑因素。 所有数据访问都是实时的,其性能完全取决于:

  • 网络延迟:Salesforce 与外部系统之间的网络往返时间。
  • 外部系统性能:外部系统的 API 响应速度至关重要。一个缓慢的 ERP 系统会直接导致 Salesforce 页面加载缓慢。
  • 查询优化:确保外部系统的 API 支持高效的分页 (Pagination) 和过滤 (Filtering)。Salesforce Connect 支持服务器驱动分页 (Server-driven) 和客户端驱动分页 (Client-driven),前者性能更佳。

2. API 调用限制

对外部对象的每次访问都会消耗 API Callout。您需要同时考虑 Salesforce 的 Callout 限制和外部系统自身的 API 速率限制。在高并发或大数据量查询的场景下(如复杂的报表),很容易达到限制,导致服务中断。因此,外部对象不适合用作高频批量数据处理的数据源。

3. 功能局限性

外部对象并非万能的,它们不支持所有标准/自定义对象的功能:

  • 自动化:不支持 Apex Triggers。虽然可以通过 Flow 等工具在某些场景下进行操作,但功能受限。
  • 报表和仪表板:外部对象可以用于报表,但存在限制。例如,它们不能作为某些报表类型的主对象,并且报表性能直接受外部系统影响。
  • 公式和汇总:不支持跨越外部对象和标准/自定义对象关系的汇总字段 (Roll-up Summary Fields)。
  • 数据共享:外部对象的可见性通常由外部系统控制,Salesforce 的共享规则不直接适用。

4. 安全与认证

连接外部系统必须安全可靠。最佳实践是使用 Named Credentials (命名凭据)。它将端点 URL 和认证信息(如用户名密码、OAuth 2.0 Token)分离开来,使代码和配置中无需硬编码敏感信息,并且简化了认证流程的管理。

5. 成本

Salesforce Connect 是一个付费的附加产品,其定价通常基于每个外部数据源连接。在进行方案设计时,必须将这部分成本纳入项目预算。


总结与最佳实践

Salesforce 外部对象是数据虚拟化理念在 Salesforce 平台上的强大实现,它改变了我们思考和设计数据集成策略的方式。它让“数据在需要时才获取”成为可能,极大地提升了系统的灵活性和实时性。

作为一名 Salesforce 架构师,我总结出以下最佳实践和决策指南:

  1. 明确使用场景
    • 优先使用外部对象:当数据量巨大、实时性要求高、数据变化频繁,或者由于合规性要求不能将数据存储在 Salesforce 时。
    • 考虑传统ETL:当需要对数据进行复杂转换、需要支持强大的离线分析和报表、或者需要利用 Salesforce 平台原生的复杂自动化(如 Apex Triggers)时。
  2. 选择正确的适配器
    • 如果外部系统支持 OData,始终优先选择 OData 适配器。这是最快、最稳定、维护成本最低的方式。
    • 当需要连接其他 Salesforce 组织时,使用 Cross-Org 适配器。
    • 仅在没有其他选择时,才诉诸于 Apex 自定义适配器。虽然它功能最强大,但开发和长期维护的成本也最高。
  3. 性能是设计的核心:在设计阶段就必须与外部系统的所有者合作,进行严格的 API 性能测试。设计时要考虑到查询的过滤和分页策略,避免全表扫描。
  4. 安全第一:始终通过命名凭据来管理与外部系统的连接,绝不将认证信息硬编码在代码或配置中。
  5. 管理用户期望:向业务方和最终用户清晰地沟通外部对象的性能预期和功能限制。让他们明白,对外部对象的操作速度可能与原生 Salesforce 对象不同。

总而言之,Salesforce 外部对象不是一个可以随意使用的“银弹”,而是一个需要精心规划和设计的架构组件。当用在正确的场景下,它可以极大地简化集成架构,降低数据冗余,为用户提供无缝、实时的统一数据视图,最终为企业创造巨大的商业价值。

评论

此博客中的热门博文

Salesforce Einstein AI 编程实践:开发者视角下的智能预测

Salesforce 登录取证:深入解析用户访问监控与安全

Salesforce Experience Cloud 技术深度解析:构建社区站点 (Community Sites)