精通 Salesforce 非锁定软件包:模块化应用开发的开发者指南

背景与应用场景

我是一名 Salesforce 开发人员。在我的职业生涯中,我见证了 Salesforce 平台开发的演变。从最初依赖变更集 (Change Sets) 进行部署,到后来使用 ANT 迁移工具,再到如今以 Salesforce DX (SFDX) 为核心的现代化开发模式,我们手中的工具变得越来越强大。在这一演进过程中,Unlocked Packages (非锁定软件包) 无疑是一项革命性的功能,它彻底改变了我们组织、分发和管理元数据的方式。

在 Unlocked Packages 出现之前,我们主要有两种方式在组织之间移动代码和配置:

1. 变更集 (Change Sets): 这是 Salesforce 内置的部署工具。虽然简单易用,但它存在诸多痛点:手动添加组件过程繁琐且容易出错、无法进行版本控制、难以自动化、处理复杂依赖关系时非常痛苦。对于大型项目,变更集简直是一场噩梦。

2. 受管软件包 (Managed Packages): 主要由 ISV (独立软件供应商) 用来在 AppExchange 上分发其商业应用。它提供了 IP 保护、可升级性和版本控制。但对于企业内部开发而言,它过于“封闭”,一旦组件被打包,就无法在安装组织中直接修改,这限制了其在内部敏捷开发场景中的灵活性。

Unlocked Packages 的出现正是为了解决上述问题。它借鉴了受管软件包的版本控制和依赖管理能力,同时又提供了“开放”的特性——安装后,其内部的元数据(如 Apex 类、Visualforce 页面、对象等)可以像普通元数据一样在订阅者组织中进行修改。这为企业内部开发团队提供了一种全新的、基于源码驱动的、模块化的应用生命周期管理 (ALM) 方式。

核心应用场景:

  • 企业内部应用分发: 假设一个大型企业拥有多个 Salesforce 组织(例如,按区域或业务线划分)。开发团队可以将在一个中央代码库中开发的核心功能(如一个定制的审批框架、一个集成的工具库)打包成 Unlocked Packages,然后分发到所有需要的组织中。这确保了代码的一致性,并极大地简化了维护和升级过程。
  • 大型单体应用的模块化拆分: 许多长期存在的 Salesforce 组织都面临着“快乐的汤”(Happy Soup) 问题,即所有代码和配置都混杂在一起,难以维护和扩展。通过 Unlocked Packages,我们可以将这个庞大的单体应用逐步拆分为多个独立的、功能内聚的模块(例如,销售模块、服务模块、财务模块)。每个模块都是一个独立的 Unlocked Package,拥有自己的版本和生命周期,这使得团队可以并行开发,降低了部署风险。
  • 实现真正的持续集成/持续部署 (CI/CD): Unlocked Packages 是现代 DevOps 实践的基石。整个打包、版本控制和部署过程都可以通过 Salesforce CLI (SFDX) 实现自动化。我们可以轻松地将其集成到 Jenkins, GitLab CI, GitHub Actions 等 CI/CD 工具链中,实现从代码提交到自动测试,再到软件包版本创建和部署的完整自动化流程。

原理说明

要理解 Unlocked Packages 的工作原理,我们必须首先了解其所依赖的核心概念和工具——Salesforce DX (SFDX) 和 Dev Hub (开发中心)。

Dev Hub 是一个特殊的 Salesforce 组织,它充当着所有开发活动(包括 Scratch Orgs 和 Unlocked Packages)的“指挥中心”。你必须在你的生产组织或一个独立的商业组织中启用 Dev Hub 功能。一旦启用,这个组织就获得了创建和管理软件包、软件包版本以及 Scratch Orgs 的能力。

Unlocked Packages 的整个生命周期都围绕着 SFDX CLI 和 Dev Hub 进行。其核心工作流可以概括为以下几个步骤:

1. 项目结构与定义

一个 SFDX 项目的根目录中必须包含一个名为 sfdx-project.json 的配置文件。这个文件是整个项目的“蓝图”,它定义了项目包含哪些软件包、软件包的路径、依赖关系以及命名空间等关键信息。一个典型的配置如下所示:

{
  "packageDirectories": [
    {
      "path": "force-app",
      "default": true,
      "package": "my-app-base",
      "versionName": "ver 0.1",
      "versionNumber": "0.1.0.NEXT",
      "dependencies": []
    },
    {
      "path": "force-app-feature-x",
      "package": "my-app-feature-x",
      "versionName": "ver 0.1",
      "versionNumber": "0.1.0.NEXT",
      "dependencies": [
        {
          "package": "my-app-base",
          "versionNumber": "0.1.0.LATEST"
        }
      ]
    }
  ],
  "namespace": "",
  "sfdcLoginUrl": "https://login.salesforce.com",
  "sourceApiVersion": "58.0",
  "packageAliases": {
    "my-app-base": "0Ho...1",
    "my-app-feature-x": "0Ho...2"
  }
}

在这个例子中,我们定义了两个 Unlocked Packages:my-app-basemy-app-feature-x。并且,my-app-feature-x 明确依赖于 my-app-base 的最新版本。这种明确的依赖关系声明是 Unlocked Packages 的强大之处,它确保了在安装时依赖项会被正确处理。

2. 软件包生命周期

一个 Unlocked Package 的生命周期分为几个关键阶段:

  • 创建 (Create): 在 Dev Hub 中注册一个软件包记录。这个操作只执行一次,会生成一个唯一的软件包 ID (以 0Ho 开头),并将其与你的项目关联起来。
  • 创建版本 (Create Version): 这是最频繁的操作。每次你准备好发布一个新版本的代码时,你都会创建一个软件包版本。这会获取你在 sfdx-project.json 中指定的路径下的所有元数据,将其打包成一个不可变的快照,并上传到 Dev Hub。每个版本都会有一个唯一的版本 ID (以 04t 开头)。版本分为两种类型:
    • Beta 版本: 用于开发和测试周期。它们不能被安装到生产组织中,并且有过期时间(默认为30天)。这非常适合在 CI/CD 流程中用于在沙盒中进行自动化测试。
    • Released 版本: 在经过充分测试后,你可以将一个 Beta 版本“提升”(Promote) 为 Released 版本。只有 Released 版本才能被安装到生产组织中,并且它们永远不会过期。一个版本一旦被提升,就不能再被修改或删除。
  • 安装 (Install): 使用软件包版本 ID (04t...) 将一个特定版本的软件包安装到任何组织中(沙盒、生产等)。SFDX 会自动处理依赖关系,如果你的软件包依赖于另一个软件包,它会确保依赖项首先被安装。
  • 升级 (Upgrade): 安装一个更高版本号的新版本即可实现升级。Salesforce 平台会自动处理新旧版本之间的元数据变更。

通过这个结构化的生命周期,开发团队可以获得前所未有的控制力和可追溯性。每一次部署都是一个有明确版本号、有确定元数据内容的不可变构件,这极大地降低了部署的风险和不确定性。


示例代码

以下所有命令均来自 Salesforce DX Developer Guide,并假设你已经通过 SFDX CLI 授权了你的 Dev Hub 组织和目标组织。

1. 创建一个 Unlocked Package

这个命令在 Dev Hub 中注册你的软件包。你只需要为每个软件包执行一次。我们为 sfdx-project.json 中定义的 my-app-base 创建一个软件包。

注释:

  • --name (-n): 软件包的名称,必须是唯一的。
  • --packagetype (-t): 指定软件包类型为 Unlocked
  • --path (-d): 包含该软件包元数据的目录路径。
  • --targetdevhubusername (-v): 你的 Dev Hub 组织的别名。
sfdx force:package:create --name "My App Base" --packagetype Unlocked --path force-app --targetdevhubusername MyDevHub

执行成功后,SFDX 会自动更新 sfdx-project.json 文件,在 packageAliases 部分添加一个别名,将 "My App Base" (或你在 sfdx-project.json 中定义的包名) 映射到一个 0Ho ID。

2. 创建一个软件包版本

在对代码进行修改和测试后,下一步是创建一个不可变的软件包版本。这是一个异步过程,可能需要几分钟时间。

注释:

  • --package (-p): 要创建版本的软件包的名称或别名。
  • --installationkey (-k): 为软件包设置一个安装密码,以保护其不被随意安装。`--installationkeybypass` 可以在安装时绕过密码,常用于自动化脚本。
  • --wait (-w): 指定 CLI 等待该异步命令完成的最长时间(分钟)。
  • --codecoverage: 强制计算 Apex 代码覆盖率,并要求测试覆盖率不低于 75%(除非 Apex 类中没有测试方法),否则版本创建会失败。这是保证代码质量的关键步骤。
sfdx force:package:version:create --package "my-app-base" --installationkey "YourPassword123" --wait 10 --codecoverage

成功后,你会得到一个以 04t 开头的 Subscriber Package Version ID,例如 04t...。这就是你将用来安装这个特定版本的唯一标识符。

3. 将 Beta 版本提升为 Released 版本

在你对 Beta 版本在沙盒中进行了充分的测试,并准备将其部署到生产环境时,你需要将其提升 (Promote)。

注释:

  • --package (-p): 你要提升的软件包版本 ID (04t...)。
  • --targetdevhubusername (-v): 你的 Dev Hub 组织的别名。
sfdx force:package:version:promote --package "04t...SubscriberPackageVersionId" --targetdevhubusername MyDevHub

注意:此操作是不可逆的。一旦版本被提升,就不能再修改或删除。

4. 安装软件包版本

最后,将软件包安装到目标组织(如 UAT 沙盒或生产组织)。

注释:

  • --package (-p): 要安装的软件包版本 ID (04t...)。
  • --targetusername (-u): 目标组织的别名。
  • --installationkey (-k): 在创建版本时设置的安装密码。
  • --wait (-w): 等待安装完成的最长时间。
  • --publishwait (-b): 等待软件包在目标组织中可用的最长时间。这对于自动化脚本很重要,确保下一步操作前软件包已完全就绪。
sfdx force:package:install --package "04t...SubscriberPackageVersionId" --targetusername MyUATOrg --installationkey "YourPassword123" --wait 10 --publishwait 10

注意事项

作为一名开发人员,在使用 Unlocked Packages 时,你需要特别注意以下几点:

  • 权限和设置: 确保你的 Dev Hub 组织已正确启用,并且执行操作的用户拥有 "Salesforce DX" 权限集。目标组织也需要正确设置,允许安装来自你 Dev Hub 的软件包。
  • 依赖管理: Unlocked Packages 的依赖关系非常强大,但也很严格。你必须按照正确的顺序安装和升级依赖包。如果在 sfdx-project.json 中定义了依赖,SFDX 会尝试自动处理,但复杂的依赖链(A->B->C)可能会导致部署时间变长,且一旦某个环节出错,排查会比较困难。
  • 破坏性变更 (Destructive Changes): Unlocked Packages 不会自动处理元数据的删除。如果你在一个新版本中删除了一个 Apex 类或一个自定义字段,升级软件包并不会自动从目标组织中删除它。你必须手动执行删除操作,或者在部署后运行一个清理脚本。这是为了防止意外删除生产环境中的数据和配置。
  • - 元数据覆盖范围: 并非所有的 Salesforce 元数据类型都支持打包。在开始项目前,务必查阅 Salesforce官方的元数据覆盖率报告,确认你需要的组件是否被支持。如果不支持,你可能需要将这部分元数据作为预部署或后部署步骤来单独处理。
  • API 限制: Dev Hub 组织对每天可以创建的软件包版本数量有限制(通常是 200 个,但具体取决于你的许可证)。在设计 CI/CD 流程时,要考虑到这个限制,避免在每次代码提交时都创建一个版本,而是应该在合并到主分支或特定发布分支时才创建。

总结与最佳实践

Unlocked Packages 是 Salesforce 平台上实现现代化、模块化和自动化应用生命周期管理的关键技术。它让开发团队能够像对待传统的软件开发一样,以一种结构化、可重复和可靠的方式来构建和交付 Salesforce 应用。

作为开发者,我们应遵循以下最佳实践来最大化其价值:

  1. 拥抱模块化 (Embrace Modularity): 努力将你的应用拆分为更小的、功能内聚的软件包。一个好的原则是“高内聚,低耦合”。例如,创建一个基础包包含共享的 Apex 工具类和基础对象,然后为每个核心业务功能(如订单管理、发票处理)创建独立的包。这使得团队可以独立工作,并降低了变更的影响范围。
  2. 源码是唯一的真相 (Source of Truth): 坚持源码驱动的开发模式。所有变更都应该首先在版本控制系统(如 Git)中进行,然后通过 SFDX 推送到 Scratch Org 进行开发和测试,最后通过 Unlocked Packages 进行部署。避免直接在沙盒或生产环境中进行修改,这会破坏源码的权威性。
  3. 自动化一切 (Automate Everything): 将软件包的创建、版本控制和部署流程完全集成到你的 CI/CD 管道中。自动化可以减少人为错误,提高部署频率,并确保每次部署都是可预测和可重复的。使用 Beta 版本进行自动化测试,只有通过所有测试的版本才能被提升为 Released 版本。
  4. 清晰的版本策略: 采用语义化版本控制(Semantic Versioning, SemVer),例如 MAJOR.MINOR.PATCH。这有助于清晰地传达每个版本变更的性质。MAJOR 版本表示有不兼容的 API 变更,MINOR 版本表示增加了向后兼容的功能,PATCH 版本表示做了向后兼容的 bug 修复。
  5. 管理好你的依赖:sfdx-project.json 中明确、清晰地定义软件包之间的依赖关系。定期审查和更新这些依赖,避免创建过于复杂或循环的依赖链。

总而言之,从变更集和 ANT 迁移工具转向 Unlocked Packages 是一种思维模式的转变。它要求我们以更严谨、更工程化的方式来对待 Salesforce 开发。虽然初期有一定的学习曲线,但它带来的长期收益——更高的开发效率、更可靠的部署质量和更易于维护的系统——是无与伦比的。作为 Salesforce 开发人员,掌握 Unlocked Packages 是我们职业技能库中一项必不可少的核心能力。

评论

此博客中的热门博文

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

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

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