精通 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-base
和 my-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 应用。
作为开发者,我们应遵循以下最佳实践来最大化其价值:
- 拥抱模块化 (Embrace Modularity): 努力将你的应用拆分为更小的、功能内聚的软件包。一个好的原则是“高内聚,低耦合”。例如,创建一个基础包包含共享的 Apex 工具类和基础对象,然后为每个核心业务功能(如订单管理、发票处理)创建独立的包。这使得团队可以独立工作,并降低了变更的影响范围。
- 源码是唯一的真相 (Source of Truth): 坚持源码驱动的开发模式。所有变更都应该首先在版本控制系统(如 Git)中进行,然后通过 SFDX 推送到 Scratch Org 进行开发和测试,最后通过 Unlocked Packages 进行部署。避免直接在沙盒或生产环境中进行修改,这会破坏源码的权威性。
- 自动化一切 (Automate Everything): 将软件包的创建、版本控制和部署流程完全集成到你的 CI/CD 管道中。自动化可以减少人为错误,提高部署频率,并确保每次部署都是可预测和可重复的。使用 Beta 版本进行自动化测试,只有通过所有测试的版本才能被提升为 Released 版本。
- 清晰的版本策略: 采用语义化版本控制(Semantic Versioning, SemVer),例如
MAJOR.MINOR.PATCH
。这有助于清晰地传达每个版本变更的性质。MAJOR 版本表示有不兼容的 API 变更,MINOR 版本表示增加了向后兼容的功能,PATCH 版本表示做了向后兼容的 bug 修复。 - 管理好你的依赖: 在
sfdx-project.json
中明确、清晰地定义软件包之间的依赖关系。定期审查和更新这些依赖,避免创建过于复杂或循环的依赖链。
总而言之,从变更集和 ANT 迁移工具转向 Unlocked Packages 是一种思维模式的转变。它要求我们以更严谨、更工程化的方式来对待 Salesforce 开发。虽然初期有一定的学习曲线,但它带来的长期收益——更高的开发效率、更可靠的部署质量和更易于维护的系统——是无与伦比的。作为 Salesforce 开发人员,掌握 Unlocked Packages 是我们职业技能库中一项必不可少的核心能力。
评论
发表评论