精通 Salesforce 外部对象:深入解析 Salesforce Connect 的无缝数据集成

背景与应用场景

作为一名 Salesforce 集成工程师,我的日常工作就是连接孤岛,让数据在不同系统间自由、高效地流动。在企业生态中,数据往往分散在各种系统中:核心业务数据可能在 SAP 或 Oracle 等 ERP 系统中,订单历史可能存放在自研的后台服务,而产品库存信息则由另一个独立的供应链系统管理。将所有这些数据都同步到 Salesforce 中不仅成本高昂,而且会引发数据一致性和时效性的问题。

传统的解决方案是采用 ETL (Extract, Transform, Load) 工具,定期将外部数据抽取、转换并加载到 Salesforce 的自定义对象中。这种数据同步 (Data Synchronization) 模式在某些场景下是必要的,但它也带来了几个挑战:

  • 数据延迟: ETL 过程是周期性的,用户在 Salesforce 中看到的数据可能不是最新的。
  • 存储成本: 在 Salesforce 中存储大量外部数据会消耗宝贵的数据存储空间。
  • 维护复杂性: 需要构建和维护复杂的 ETL 作业,确保数据同步的稳定性和准确性。

为了解决这些问题,Salesforce 提供了另一种强大的集成模式:数据虚拟化 (Data Virtualization)。而实现这一模式的核心技术就是 Salesforce Connect,其具体表现形式便是 External Objects (外部对象)。

External Objects 是一种特殊的 Salesforce 对象,它的元数据(字段、布局等)定义在 Salesforce 中,但其数据记录本身并不存储在 Salesforce,而是实时地存放在外部系统中。当用户访问外部对象的数据时,Salesforce 会在后台实时向外部系统发起请求,获取数据并呈现在用户界面上。这种“按需查看”的模式完美地解决了数据延迟和存储成本的问题。

核心应用场景:

  • 360度客户视图: 在客户 (Account) 页面上,直接展示来自 ERP 系统的实时订单历史、发货状态或财务账款,而无需将这些海量数据复制到 Salesforce。
  • 实时库存查询: 销售人员在创建订单 (Order) 时,可以实时查询外部仓储系统中的产品库存,确保订单的有效性。
  • 合规与数据驻留: 对于某些有严格数据驻留要求的行业(如金融、医疗),可以将敏感数据保留在本地服务器上,同时允许 Salesforce 用户通过外部对象进行安全的只读访问。
  • 简化集成: 对于那些只需要“查看”而不需要在 Salesforce 中进行复杂处理的外部数据,使用外部对象可以极大地简化集成架构,避免开发和维护复杂的同步逻辑。

原理说明

要理解 External Objects,我们必须先理解其背后的引擎——Salesforce Connect。Salesforce Connect 作为一个框架,定义了 Salesforce 如何与外部数据源进行通信。其工作流程可以分解为以下几个关键部分:

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

这是集成的起点。管理员需要在 Salesforce 中创建一个外部数据源,配置连接到外部系统所需的所有信息,包括服务器 URL、认证凭据、协议类型等。Salesforce Connect 支持多种适配器 (Adapters) 来连接不同类型的外部系统:

  • OData 2.0 / OData 4.0: OData (Open Data Protocol) 是目前最常用和推荐的适配器。它是一个基于 RESTful API 的开放标准,专门用于查询和操作数据集。如果你的外部系统(如 SAP, SharePoint, 或者自定义服务)能够暴露 OData 端点,集成过程会非常顺畅。
  • Cross-Org: 这个适配器专门用于连接另一个 Salesforce 组织。它使得在一个组织中可以实时访问另一个组织的数据,非常适用于企业内部有多个 Salesforce 实例的场景。
  • Custom (Apex Connector Framework): 当外部系统不提供 OData 接口时,这就成了终极解决方案。开发人员可以利用 Apex 编写自定义适配器,通过实现一系列预定义的接口 (DataSource.ConnectionDataSource.Provider),来教会 Salesforce 如何与任何基于 REST、SOAP 或其他协议的外部系统进行通信。这为集成提供了无限的灵活性,但开发和维护成本也最高。

2. 外部对象的定义与同步

在配置好外部数据源后,管理员可以点击“验证和同步 (Validate and Sync)”按钮。Salesforce Connect 会连接到外部数据源,并获取其暴露的数据表或集合的元数据。例如,对于一个 OData 服务,Salesforce 会获取其暴露的实体 (Entities) 列表。管理员可以选择需要引入的表,Salesforce 会自动为它们创建对应的 External Objects。这些对象的 API 名称会以后缀 __x 结尾,例如外部的 "Orders" 表会变成 Salesforce 中的 Orders__x 对象。

3. 实时数据交互流程

当用户或自动化流程与外部对象交互时,整个过程是实时的、透明的:

  1. 用户操作: 用户在 Salesforce 中打开一个列表视图、报告,或者通过 SOQL 查询一个 Orders__x 对象。
  2. 查询转换: Salesforce 平台将这个操作(例如 SOQL 查询)实时转换为外部系统能够理解的查询。如果使用 OData 适配器,这个 SOQL 会被翻译成一个 OData 查询(例如,包含 $filter, $select, $top, $skip 等参数的 HTTP GET 请求)。
  3. 安全调用: Salesforce Connect 通过外部数据源中配置的认证方式,向外部系统的 API 端点发起这个经过转换的查询请求。
  4. 外部系统响应: 外部系统处理该请求,并以指定的格式(如 JSON 或 XML)返回数据。
  5. 数据呈现: Salesforce Connect 接收到响应数据,将其解析并呈现给用户,仿佛这些数据一直存储在 Salesforce 中一样。

整个过程中,数据只是“流经”Salesforce,而不会被持久化存储,这正是数据虚拟化的精髓所在。


示例代码

尽管 External Objects 的许多配置都是声明式的,但在实际的业务场景中,我们经常需要通过 Apex 代码来与这些外部数据进行交互。例如,在一个自定义的 Lightning Web Component 中,我们可能需要根据当前客户的信息,动态查询其在外部 ERP 系统中的订单。

假设我们已经配置好了一个名为 ERP_System 的外部数据源,并同步了一个名为 Order__x 的外部对象。这个外部对象通过一个间接查找关系 (Indirect Lookup Relationship) 关联到 Salesforce 的标准客户 (Account) 对象。Order__x 上有一个字段 Customer_ID__c,它对应 Account 对象的 Legacy_ERP_ID__c 字段(该字段必须被标记为 External IDUnique)。

以下 Apex 代码展示了如何通过 SOQL 查询与特定客户关联的所有外部订单:

// Apex Class to fetch external orders for a given Account
public with sharing class ExternalOrderService {

    /**
     * @description Queries for external orders related to a specific Account record.
     * @param accountId The ID of the standard Account object.
     * @return A list of external order records (Order__x).
     */
    public static List<Order__x> getOrdersForAccount(Id accountId) {
        
        // First, query the Account to get the External ID value used for the indirect lookup.
        // The relationship relies on this unique key, not the Salesforce Record ID.
        Account acc = [SELECT Id, Name, Legacy_ERP_ID__c FROM Account WHERE Id = :accountId];

        // Check if the legacy ID exists. If not, there can be no related external orders.
        if (acc == null || String.isBlank(acc.Legacy_ERP_ID__c)) {
            // Return an empty list to prevent errors.
            return new List<Order__x>();
        }

        // Use a SOQL query to fetch records from the external object.
        // Notice the API name ends with '__x'.
        // The WHERE clause filters on the relationship field. Salesforce Connect
        // translates this into a query on the external system (e.g., OData $filter).
        List<Order__x> externalOrders = [
            SELECT Id, OrderNumber__c, TotalAmount__c, Status__c, OrderDate__c 
            FROM Order__x 
            WHERE Account__r.Legacy_ERP_ID__c = :acc.Legacy_ERP_ID__c
            ORDER BY OrderDate__c DESC
            LIMIT 100
        ];

        return externalOrders;
    }
}

代码注释详解:

  • 第 15-16 行: 查询外部对象和查询标准/自定义对象在语法上完全相同。关键是使用外部对象的 API 名称,即 Order__x
  • 第 19-20 行: 这里的查询条件是关键。我们没有直接用 AccountId 来过滤,而是通过间接查找关系字段 Account__r,并进一步指定其关联的外部 ID 字段 Legacy_ERP_ID__c。Salesforce Connect 会智能地将这个查询转换为对外部系统 Order 表的过滤,条件是 Customer_ID__c 等于我们提供的 Legacy_ERP_ID__c 的值。
  • 第 22 行: LIMITORDER BY 等标准 SOQL 子句同样适用于外部对象,前提是外部系统支持相应的排序和分页功能。这对于性能优化至关重要。

如果外部数据源被配置为可写 (Writable),我们甚至可以执行 DML 操作。例如,更新一个外部订单的状态:

// Example of updating a writable external object record.
// This requires the External Data Source to have "Writable External Objects" enabled.
// The external system's API must also support PATCH or PUT requests.

// Assume we have an external order record we want to update.
Order__x orderToUpdate = [SELECT Id, Status__c FROM Order__x WHERE OrderNumber__c = 'ORD-12345' LIMIT 1];

if (orderToUpdate != null) {
    orderToUpdate.Status__c = 'Shipped';
    
    // The standard DML 'update' statement works on writable external objects.
    // Salesforce Connect translates this into an API call (e.g., HTTP PATCH) to the external system.
    try {
        update orderToUpdate;
    } catch (DmlException e) {
        // Handle potential callout exceptions or errors from the external system.
        System.debug('Error updating external object: ' + e.getMessage());
    }
}

注意事项

虽然 External Objects 功能强大,但作为集成工程师,我们必须清醒地认识到它的局限性和需要注意的关键点:

1. 性能与延迟

所有的数据访问都涉及一次实时的网络调用 (Callout)。如果外部系统响应缓慢或网络延迟高,用户在 Salesforce 中的体验会非常差。因此,外部对象的性能瓶颈通常不在 Salesforce,而在外部系统。务必确保外部系统的 API 经过优化,能够快速响应过滤和分页请求。

2. API 限制

  • Salesforce Connect 限制: Salesforce 对每个组织通过 Salesforce Connect 发出的调用有小时限制(例如,OData 适配器每小时 20,000 次调用,具体请查阅最新的官方文档)。高流量的应用场景可能会触及此上限。
  • 外部系统限制: 外部系统自身通常也有 API 速率限制。频繁的查询或大量用户访问可能会导致外部系统节流或拒绝服务。

3. 功能与数据模型限制

  • 不支持的关系: 外部对象不支持主从关系 (Master-Detail Relationship),也不能成为标准或自定义对象的主控方。
  • 不支持的自动化: 不能在外部对象上创建 Apex 触发器 (Triggers)、流 (Flows) 或验证规则 (Validation Rules)。所有业务逻辑和数据验证都必须在外部系统中实现。
  • 报表和分析限制: 虽然外部对象可以包含在 Salesforce 报表中,但对大量数据进行分组、聚合和汇总的性能可能很差,因为这会产生大量对外部系统的查询。对于复杂的分析需求,数据同步模式可能更合适。
  • 不支持公式字段: 涉及跨对象关系的公式字段在外部对象上通常不受支持。

4. 安全与权限

Salesforce 中的简档 (Profile) 和权限集 (Permission Set) 可以控制用户对外部对象及其字段的访问权限 (CRUD/FLS)。然而,数据的最终访问权限由外部系统决定。在配置外部数据源时,必须选择合适的身份验证类型 (Identity Type)

  • Per User (每用户): Salesforce 会将当前用户的身份信息(例如通过 OAuth 2.0)传递给外部系统。外部系统根据该用户的权限来决定返回哪些数据。这提供了精细的、基于用户的访问控制。
  • Named Principal (指定负责人): 所有 Salesforce 用户都通过一个共享的服务账户来访问外部系统。这种方式配置简单,但无法在外部系统层面区分不同的 Salesforce 用户。

总结与最佳实践

External Objects 和 Salesforce Connect 是 Salesforce 集成工具箱中一把锋利的“瑞士军刀”,它为我们提供了强大的数据虚拟化能力,让我们能够以最小的成本和复杂性实现跨系统的数据整合。

总结一下,何时应该优先考虑使用 External Objects?

  • 当需要实时访问外部数据,且数据延迟不可接受时。
  • 当外部数据集非常庞大,将其全部复制到 Salesforce 中不切实际或成本过高时。
  • 当数据变化非常频繁,ETL 同步的频率无法满足业务需求时。
  • 当集成需求主要是“读取”和“少量写入”,而不需要在 Salesforce 中对这些数据执行复杂的业务逻辑时。

反之,何时应该选择传统的数据同步方案?

  • 当需要对数据进行复杂的报表和分析时。
  • 当需要在数据变化时触发 Salesforce 内部的自动化流程(如触发器、流)时。
  • 当外部系统响应慢或不可靠,需要将数据缓存在 Salesforce 以保证性能和可用性时。

最佳实践:

  1. 优化外部 API: 在设计或使用外部系统的 API 时,确保其支持高效的服务端过滤、排序和分页。这是保证 Salesforce Connect 性能的基石。
  2. 明智地选择关系: 充分利用间接查找 (Indirect Lookup) 关系,将外部数据无缝地嵌入到 Salesforce 的核心业务对象(如客户、联系人)中,为用户提供统一的视图。
  3. 缓存策略: 对于那些不经常变化但访问频繁的外部数据,可以考虑结合使用 Apex 调用和平台缓存 (Platform Cache),以减少对外部系统的调用次数,提升性能。
  4. 监控和测试: 在部署前,进行严格的性能和负载测试。上线后,持续监控 Salesforce Connect 的调用限制和外部系统的 API 使用情况,避免服务中断。
  5. 选择正确的适配器: 优先使用标准的 OData 适配器。仅在别无选择时,才投入资源开发自定义的 Apex 适配器。

作为一名集成工程师,精通 External Objects 不仅意味着掌握一项技术,更意味着能够为企业设计出更优雅、更高效、更具成本效益的数据架构。它让我们从繁琐的数据搬运工作中解放出来,专注于构建真正有价值的业务连接。

评论

此博客中的热门博文

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

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

Salesforce Data Loader 全方位指南:数据迁移与管理的最佳实践