Salesforce Connect:架构师指南之无缝外部数据集成
背景与应用场景
作为一名 Salesforce 架构师,我在设计企业级解决方案时,面临的核心挑战之一是如何优雅地整合分散在不同系统中的数据。企业的数据生态系统通常是复杂且异构的,关键业务数据可能存储在 ERP 系统(如 SAP 或 Oracle)、内部数据库、专有系统或云服务中。传统的数据集成方法,如 ETL (Extract, Transform, Load),虽然成熟,但存在数据延迟、存储成本高、维护复杂等问题。当业务需要实时或接近实时地访问外部数据时,ETL 模式便显得力不从心。
正是在这样的背景下,Salesforce Connect 应运而生。它是一种强大的数据虚拟化 (Data Virtualization) 服务,允许您在 Salesforce 内部以“外部对象” (External Objects) 的形式,实时查看、搜索和修改存储在外部系统中的数据,而无需将这些数据复制并存储到 Salesforce 中。这种模式彻底改变了我们对数据集成的看法。
典型的应用场景包括:
- 订单管理:销售人员在客户 (Account) 页面上,需要查看该客户的所有历史订单和发货状态。这些数据通常存储在后端的 ERP 系统中。通过 Salesforce Connect,我们可以将 ERP 的订单表映射为一个外部对象,并与 Account建立关系,让销售人员在 Salesforce UI 中直接查看实时订单信息,而无需切换系统。
- 产品目录:对于拥有庞大且频繁更新的产品目录的电商或制造企业,将所有产品数据同步到 Salesforce 会占用大量存储空间且难以维护。使用 Salesforce Connect,可以直接连接到产品信息管理 (PIM) 系统,实时展示产品详情、库存和价格。
- 财务数据:财务团队可能需要在 Salesforce 中查看客户的支付历史、信用额度等敏感信息,这些信息通常由财务系统严格管理。通过 Salesforce Connect,可以授予特定用户只读权限,让他们在不直接访问财务系统的情况下,获取所需的数据视图。
- 遗留系统集成:在企业进行系统现代化改造的过程中,某些遗留系统可能短期内无法被替代。Salesforce Connect 提供了一种低侵入性的方式,将这些系统的数据暴露给现代化的 Salesforce 应用,实现平滑过渡。
从架构师的角度来看,Salesforce Connect 提供了一种轻量级、敏捷且可扩展的集成模式,它补充了传统的 ETL 和 API 集成方案,为我们构建“单一客户视图” (Single View of the Customer) 提供了更多选择。
原理说明
Salesforce Connect 的核心是“按需访问”,而不是“数据复制”。当用户或系统在 Salesforce 中查询一个外部对象时,Salesforce 会实时将这个请求转化为对外部系统的 API 调用。数据在传输过程中被处理,并在 Salesforce UI 中呈现,但它本身并不持久化存储在 Salesforce 的数据库中。这个过程对最终用户是完全透明的,他们操作外部对象的感觉与操作标准或自定义对象几乎没有区别。
要实现这一点,Salesforce Connect 依赖于几个关键组件:
1. 外部数据源 (External Data Sources)
这是连接的起点。您需要在 Salesforce 中定义一个外部数据源,它包含了连接到外部系统所需的所有信息,例如服务地址 (URL)、身份验证凭据、协议类型等。Salesforce Connect 主要支持以下几种适配器 (Adapters):
- OData 2.0/4.0:OData (Open Data Protocol) 是一种基于 HTTP 和 RESTful API 的开放标准,用于构建和消费数据服务。如果您的外部系统支持 OData 协议,那么集成过程将非常简单,大部分工作可以通过声明式配置完成。Salesforce 会自动发现 OData 服务中的数据实体,并允许您选择性地将其同步为外部对象。
- Salesforce (Cross-Org):允许您连接到另一个 Salesforce Org,将另一个 Org 中的对象作为外部对象在当前 Org 中使用。这对于大型企业中不同业务单元之间的 Org 整合非常有用。
- Custom (Apex Connector Framework):当外部系统不支持 OData 时,这便是终极解决方案。您可以使用 Apex 编写自定义适配器,通过 Apex Connector Framework 来处理与任何外部数据源的连接、查询和数据转换。这为集成提供了无限的灵活性,但也要求更高的开发成本。
2. 外部对象 (External Objects)
外部对象是外部数据在 Salesforce 中的元数据表示。它们拥有与标准/自定义对象类似的 API 名称、字段、页面布局和关系。当您通过外部数据源同步一个外部实体时,Salesforce 会为您创建一个对应的外部对象。它的字段会映射到外部数据源的列。外部对象有一个特殊的标准字段叫 `External ID`,它必须唯一地标识外部系统中的每一条记录。
3. 查询转换与执行
这是 Salesforce Connect 的魔法所在。当一个用户执行 SOQL 查询、打开列表视图或查看相关列表时,Salesforce 会在幕后执行以下操作:
- 解析请求:Salesforce 的查询引擎解析针对外部对象的 SOQL 或 SOSL 请求。
- 转换为外部查询:Salesforce Connect 适配器将该请求转换为外部系统能够理解的格式。例如,对于 OData 适配器,一个 SOQL 查询 `SELECT Id, Name FROM Orders__x WHERE CustomerId__c = '001...'` 可能会被转换为一个 OData URL 查询,如 `https://erp.example.com/odata.svc/Orders?$filter=CustomerId eq '001...'&$select=Id,Name`。
- 执行调用:Salesforce 发起一个对外部系统的 API 调用 (Callout)。
- 处理响应:接收到外部系统的响应数据(通常是 JSON 或 XML 格式),适配器将其解析并映射到外部对象的字段结构中。
- 呈现结果:最终,数据在 Salesforce 用户界面或 API 响应中呈现出来。
从架构上看,Salesforce Connect 本质上是一个实时的、双向的 API 代理和数据转换引擎,它巧妙地将外部数据无缝地嵌入到 Salesforce 平台的数据模型和用户体验中。
示例代码
对于不支持 OData 的复杂场景,我们需要使用 Apex Connector Framework 创建自定义适配器。以下是一个来自 Salesforce 官方文档的简化示例,展示了如何实现一个自定义连接器来查询外部数据。这个示例假设我们要连接到一个虚构的产品数据服务。
首先,我们需要一个 `DataSource.Provider` 类,它负责声明连接器的能力,并提供连接实例。
// ProductDataSourceProvider.cls // This class declares the capabilities of our custom connector and provides connection instances. public class ProductDataSourceProvider extends DataSource.Provider { // Declares the authentication capabilities. Here, we specify 'Anonymous' access. // For real-world scenarios, you'd likely use 'Oauth' or 'Password'. public override List<DataSource.AuthenticationCapability> getAuthenticationCapabilities() { List<DataSource.AuthenticationCapability> capabilities = new List<DataSource.AuthenticationCapability>(); capabilities.add(DataSource.AuthenticationCapability.ANONYMOUS); return capabilities; } // Declares the general capabilities of the connector. // 'ROW_QUERY' indicates that the connector can query for specific rows. // 'SEARCH' indicates support for SOSL 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; } // This method is called by Salesforce to get an instance of the connection. // The connection class contains the logic for querying, searching, etc. public override DataSource.Connection getConnection(DataSource.ConnectionParams connectionParams) { return new ProductDataSourceConnection(connectionParams); } }
接下来是 `DataSource.Connection` 类,这是实现数据交互逻辑的核心。我们需要重写 `query()` 和 `sync()` 方法。
// ProductDataSourceConnection.cls // This class handles the actual data fetching and synchronization logic. public class ProductDataSourceConnection extends DataSource.Connection { private DataSource.ConnectionParams connectionInfo; // Constructor to store connection parameters. public ProductDataSourceConnection(DataSource.ConnectionParams connectionParams) { this.connectionInfo = connectionParams; } // The sync method is called when an administrator clicks "Validate and Sync" // on the External Data Source page. It should return a list of tables (as DataSource.Table) // that can be mapped to External Objects. public override List<DataSource.Table> sync() { List<DataSource.Table> tables = new List<DataSource.Table>(); // Define the columns for our "Products" table. List<DataSource.Column> columns = new List<DataSource.Column>(); columns.add(DataSource.Column.text('Name', 255)); columns.add(DataSource.Column.text('SKU', 50)); columns.add(DataSource.Column.url('ProductURL')); columns.add(DataSource.Column.text('Description', 500)); // Create the table definition. 'Products' is the table name in the external system. // The first column ('Name' here) is used as the display column by default. tables.add(DataSource.Table.get('Products', 'Name', columns)); return tables; } // The query method is the heart of the connector. It's executed whenever a user // queries the external object (e.g., via a list view, report, or SOQL). public override DataSource.Results query(DataSource.QueryContext queryContext) { // In a real implementation, you would make an HTTP callout to the external service here. // For simplicity, this example returns static data. // This simulates building a query URL for the external service. String serviceUrl = 'https://api.example.com/products?q=' + queryContext.tableSelection.filter; // Create a list to hold the rows of data returned. List<Map<String, Object>> responseRows = new List<Map<String, Object>>(); // Simulate a response from the external service. Map<String, Object> row1 = new Map<String, Object>(); row1.put('Name', 'Super Widget'); row1.put('SKU', 'SW-001'); row1.put('ProductURL', 'https://example.com/products/sw001'); row1.put('Description', 'A high-quality widget.'); responseRows.add(row1); Map<String, Object> row2 = new Map<String, Object>(); row2.put('Name', 'Mega Gadget'); row2.put('SKU', 'MG-002'); row2.put('ProductURL', 'https://example.com/products/mg002'); row2.put('Description', 'A versatile gadget for all your needs.'); responseRows.add(row2); // The DataSource.TableResult contains the query results. // 'Products' is the table name, 'Name' is the external ID column. // The second argument 'Name' is the name of the column that serves as the External ID. // The third argument is the list of column names, and the fourth is the data. DataSource.TableResult result = DataSource.TableResult.get('Products', 'Name', new List<String>{'Name', 'SKU', 'ProductURL', 'Description'}, responseRows); return DataSource.Results.get(result); } }
⚠️ 官方文档依据:以上代码结构和类(`DataSource.Provider`, `DataSource.Connection`, `DataSource.Results` 等)均基于 Salesforce Apex Developer Guide 中关于 "Apex Connector Framework" 的章节。
注意事项
作为架构师,在推荐或设计 Salesforce Connect 解决方案时,必须充分考虑其限制和权衡,避免在后期遇到性能瓶颈或功能障碍。
1. 性能与 Callout 限制
- 实时性是有代价的:每个对外部对象的查询都会触发一个对外部系统的 API 调用 (Callout)。如果外部系统响应缓慢,Salesforce 的用户体验会直接受到影响。
- 高数据量场景:Salesforce Connect 在处理大量数据分页时,性能开销较大。它更适合“针尖式”查询(查询少量特定记录),而不是大规模的数据提取和分析。
- Governor Limits:使用 Apex Connector Framework 时,所有 Apex 的 Governor Limits 依然适用,包括 Callout 总数、超时时间、CPU 时间和堆大小。复杂的数据转换逻辑可能会触及这些限制。
2. 数据模型与关系
- 外部对象不能位于主从关系 (Master-Detail Relationship) 的“明细”端。
- 它们支持查找关系 (Lookup Relationship)、外部查找关系 (External Lookup Relationship) 和间接查找关系 (Indirect Lookup Relationship),正确选择关系类型对于建立数据关联至关重要。
- 公式字段、汇总字段 (Roll-up Summary Fields) 和某些自动化工具(如标准的工作流规则)在外部对象上的支持有限。
3. 查询与报表限制
- SOQL/SOSL 支持:并非所有 SOQL 子句都受支持。例如,`GROUP BY`、`COUNT(fieldname)`、`SUM()` 等聚合函数通常不受支持或依赖于外部系统的能力(例如 OData 4.0 的聚合扩展)。
- 报表:报表功能受到很大限制。外部对象通常不能作为报表的主要对象,并且在与其他对象进行连接 (Join) 时有诸多限制。对于复杂的分析和仪表板需求,将数据通过 ETL 引入到 Salesforce 或 BI 工具中可能是更好的选择。
4. 安全与认证
- Salesforce 通过简档 (Profiles) 和权限集 (Permission Sets) 控制用户对外部对象及其字段的访问权限。
- 然而,数据的最终访问权限由外部系统决定。必须在外部数据源中配置正确的认证方式(如使用命名凭据 (Named Credentials) 结合 OAuth 2.0),以确保 Salesforce 发出的调用具有适当的权限。这是一个双重安全模型,两边都必须配置正确。
总结与最佳实践
Salesforce Connect 是一个强大的、战略性的集成工具,但不应被视为所有集成问题的“万能药”。作为架构师,我们的职责是根据业务需求的具体上下文,明智地选择合适的集成模式。
最佳实践与架构建议:
- 明确使用场景:当业务需要实时访问少量、易变的外部数据,并且不希望增加 Salesforce 存储成本时,Salesforce Connect 是理想选择。例如,查看单个客户的最新订单状态。
- 避免大规模数据操作:对于需要进行复杂聚合、批量处理或深度分析的数据,传统的 ETL 模式(如通过 MuleSoft 或其他中间件将数据同步到 Salesforce 的自定义对象中)通常是更可靠、性能更好的方案。
- 优先选择 OData:如果可以影响外部系统的技术选型,优先推荐提供 OData 接口的系统。这能最大程度地利用 Salesforce 的声明式配置能力,降低开发和维护成本。
- 善用 Apex Connector Framework:当别无选择时,Apex Connector Framework 提供了连接任何系统的灵活性。在设计自定义适配器时,要特别注意性能优化、错误处理和遵循 Governor Limits。
- 混合集成模式:最成功的企业架构往往采用混合集成模式。可以结合使用 Salesforce Connect 进行实时查询,同时通过夜间的 ETL 批处理作业同步关键数据用于报表和分析,取长补短。
- 监控外部系统性能:Salesforce Connect 的性能高度依赖于外部系统的响应能力。建立对外部系统 API 的监控机制,确保其稳定性和低延迟,是保障整体解决方案健康运行的关键。
总之,Salesforce Connect 通过数据虚拟化技术,为我们提供了一种看待和处理外部数据的全新视角。它让 Salesforce 平台能够“超越”自身的边界,成为一个真正意义上的企业级数据整合与交互中心。正确地运用它,将极大地提升解决方案的敏捷性和业务价值。
评论
发表评论