trigger-refactor-pipeline
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhen to Use This Skill
何时使用此技能
Use this skill when you need to:
- Modernize legacy triggers with DML/SOQL operations inside loops
- Refactor triggers that lack clear separation of concerns
- Implement bulk-safe patterns in existing trigger code
- Generate comprehensive test coverage for refactored triggers
当你需要以下操作时,可使用此技能:
- 对循环内包含DML/SOQL操作的遗留触发器进行现代化改造
- 重构关注点分离不清晰的触发器
- 在现有触发器代码中实现批量安全模式
- 为重构后的触发器生成全面的测试覆盖率
Prerequisites
前置条件
Before starting, ensure you have:
- Salesforce CLI installed and authenticated to your target org
- Python 3.9 or higher installed
- The baseline trigger deployed (see Setup section)
开始之前,请确保你已具备:
- 已安装Salesforce CLI并完成目标组织的身份验证
- 已安装Python 3.9或更高版本
- 已部署基线触发器(请参阅设置部分)
Setup
设置步骤
Deploy the baseline anti-pattern trigger to analyze and refactor:
apex
// ❌ Anti-pattern: all logic stuffed into the trigger, with DML/SOQL in loops.
trigger OpportunityTrigger on Opportunity (before insert, before update, after update) {
// BEFORE INSERT: validate Closed Won w/ low Amount
if (Trigger.isBefore && Trigger.isInsert) {
for (Opportunity o : Trigger.new) {
if (o.StageName == 'Closed Won' && (o.Amount == null || o.Amount < 1000)) {
o.addError('Closed Won opportunities must have Amount ≥ 1000.');
}
}
}
// BEFORE UPDATE: if Stage changed, overwrite Description
if (Trigger.isBefore && Trigger.isUpdate) {
for (Opportunity o : Trigger.new) {
Opportunity oldO = Trigger.oldMap.get(o.Id);
if (o.StageName != oldO.StageName) {
o.Description = 'Stage changed from ' + oldO.StageName + ' to ' + o.StageName;
}
}
}
// AFTER UPDATE: when Stage becomes Closed Won, create a follow-up Task
if (Trigger.isAfter && Trigger.isUpdate) {
for (Opportunity o : Trigger.new) {
Opportunity oldO = Trigger.oldMap.get(o.Id);
if (o.StageName == 'Closed Won' && oldO.StageName != 'Closed Won') {
Task t = new Task(
WhatId = o.Id,
OwnerId = o.OwnerId,
Subject = 'Send thank-you',
Status = 'Not Started',
Priority = 'Normal',
ActivityDate = Date.today()
);
insert t; // ❌ DML in a loop
}
}
}
}Deploy this to your org:
bash
sf project deploy start --source-dir force-app/main/default/triggers部署用于分析和重构的基线反模式触发器:
apex
// ❌ 反模式:所有逻辑都塞进触发器,循环内包含DML/SOQL操作。
trigger OpportunityTrigger on Opportunity (before insert, before update, after update) {
// BEFORE INSERT:验证金额较低的Closed Won商机
if (Trigger.isBefore && Trigger.isInsert) {
for (Opportunity o : Trigger.new) {
if (o.StageName == 'Closed Won' && (o.Amount == null || o.Amount < 1000)) {
o.addError('Closed Won商机的金额必须≥1000。');
}
}
}
// BEFORE UPDATE:若阶段变更,覆盖描述信息
if (Trigger.isBefore && Trigger.isUpdate) {
for (Opportunity o : Trigger.new) {
Opportunity oldO = Trigger.oldMap.get(o.Id);
if (o.StageName != oldO.StageName) {
o.Description = '阶段从' + oldO.StageName + '变更为' + o.StageName;
}
}
}
// AFTER UPDATE:当阶段变为Closed Won时,创建跟进任务
if (Trigger.isAfter && Trigger.isUpdate) {
for (Opportunity o : Trigger.new) {
Opportunity oldO = Trigger.oldMap.get(o.Id);
if (o.StageName == 'Closed Won' && oldO.StageName != 'Closed Won') {
Task t = new Task(
WhatId = o.Id,
OwnerId = o.OwnerId,
Subject = '发送感谢信',
Status = '未开始',
Priority = '正常',
ActivityDate = Date.today()
);
insert t; // ❌ 循环内执行DML
}
}
}
}将其部署到你的组织:
bash
sf project deploy start --source-dir force-app/main/default/triggersStep 1: Analyze the Trigger
步骤1:分析触发器
Run the analysis script to identify anti-patterns and generate a report:
bash
python scripts/analyze_trigger.py OpportunityTriggerThe script will output:
- DML in loops - Line numbers where DML operations occur inside iteration
- SOQL in loops - Line numbers where SOQL queries occur inside iteration
- Missing bulkification - Areas where collection-based processing is needed
- Complexity score - Overall trigger complexity rating (1-10)
- Recommended approach - Suggested handler pattern based on trigger contexts
Review the analysis report before proceeding to refactoring.
运行分析脚本识别反模式并生成报告:
bash
python scripts/analyze_trigger.py OpportunityTrigger脚本将输出:
- 循环内DML操作 - 循环内执行DML操作的行号
- 循环内SOQL查询 - 循环内执行SOQL查询的行号
- 缺失批量优化 - 需要基于集合处理的区域
- 复杂度评分 - 触发器整体复杂度评级(1-10)
- 推荐方案 - 基于触发器上下文的建议处理器模式
在进行重构前,请先查看分析报告。
Step 2: Review Handler Patterns
步骤2:查看处理器模式
Consult the handler patterns reference to understand:
- Single-responsibility handlers - One handler class per trigger context
- Unified handler approach - Single handler with context methods
- Bulk collection strategies - How to aggregate DML/SOQL outside loops
- Best practices - Error handling, test boundaries, deployment order
Choose the pattern that best fits your trigger's complexity and team conventions.
参考处理器模式文档了解:
- 单一职责处理器 - 每个触发器上下文对应一个处理器类
- 统一处理器方案 - 包含上下文方法的单一处理器
- 批量集合策略 - 如何在循环外聚合DML/SOQL操作
- 最佳实践 - 错误处理、测试边界、部署顺序
选择最适合你触发器复杂度和团队规范的模式。
Step 3: Refactor the Trigger
步骤3:重构触发器
Create the handler class using the appropriate pattern from the reference guide:
- Extract logic into handler methods with descriptive names
- Implement bulk-safe collections for DML operations
- Add proper error handling using try-catch or Database methods
- Update the trigger to delegate only, passing Trigger context variables
- Preserve behavior - ensure the refactored code produces identical results
The trigger should be reduced to simple delegation:
apex
trigger OpportunityTrigger on Opportunity (before insert, before update, after update) {
OpportunityTriggerHandler handler = new OpportunityTriggerHandler();
if (Trigger.isBefore && Trigger.isInsert) {
handler.beforeInsert(Trigger.new);
}
if (Trigger.isBefore && Trigger.isUpdate) {
handler.beforeUpdate(Trigger.new, Trigger.oldMap);
}
if (Trigger.isAfter && Trigger.isUpdate) {
handler.afterUpdate(Trigger.new, Trigger.oldMap);
}
}使用参考指南中的合适模式创建处理器类:
- 提取逻辑到具有描述性名称的处理器方法中
- 实现批量安全集合用于DML操作
- 添加适当的错误处理,使用try-catch或Database方法
- 更新触发器仅做委托,传递Trigger上下文变量
- 保留原有行为 - 确保重构后的代码产生完全一致的结果
触发器应简化为仅包含委托逻辑:
apex
trigger OpportunityTrigger on Opportunity (before insert, before update, after update) {
OpportunityTriggerHandler handler = new OpportunityTriggerHandler();
if (Trigger.isBefore && Trigger.isInsert) {
handler.beforeInsert(Trigger.new);
}
if (Trigger.isBefore && Trigger.isUpdate) {
handler.beforeUpdate(Trigger.new, Trigger.oldMap);
}
if (Trigger.isAfter && Trigger.isUpdate) {
handler.afterUpdate(Trigger.new, Trigger.oldMap);
}
}Step 4: Generate Tests
步骤4:生成测试
Use the test template from to scaffold your test class:
assets/test_template.apex- Copy the template and rename for your handler
- Implement setup methods to create test data
- Write unit tests covering each handler method:
- Positive cases with valid data
- Negative cases with invalid data
- Boundary conditions
- Add bulk tests with 200+ records to verify bulkification
- Test mixed scenarios where only some records qualify for logic
Required test coverage:
- Each handler method must have at least 2 test methods (positive + negative)
- At least one bulk test with 200+ records
- Overall code coverage must be 100%
使用中的测试模板搭建你的测试类:
assets/test_template.apex- 复制模板并重命名以适配你的处理器
- 实现设置方法创建测试数据
- 编写单元测试覆盖每个处理器方法:
- 有效数据的正向用例
- 无效数据的反向用例
- 边界条件
- 添加批量测试,使用200+条记录验证批量安全性
- 测试混合场景,仅部分记录符合逻辑条件
要求的测试覆盖率:
- 每个处理器方法至少要有2个测试方法(正向+反向)
- 至少一个使用200+条记录的批量测试
- 整体代码覆盖率必须达到100%
Step 5: Deploy and Validate
步骤5:部署与验证
Deploy the refactored trigger, handler, and tests:
bash
undefined部署重构后的触发器、处理器和测试:
bash
undefinedDeploy all components
部署所有组件
sf project deploy start --source-dir force-app/main/default
sf project deploy start --source-dir force-app/main/default
Run tests
运行测试
sf apex test run --class-names OpportunityTriggerHandlerTest --result-format human --code-coverage
sf apex test run --class-names OpportunityTriggerHandlerTest --result-format human --code-coverage
Verify no regressions
验证无回归问题
sf apex test run --test-level RunLocalTests --result-format human
Validation checklist:
- [ ] All new tests pass with 100% coverage
- [ ] No new governor limit warnings in debug logs
- [ ] Existing functionality remains unchanged
- [ ] Deployment to production planned with rollback strategysf apex test run --test-level RunLocalTests --result-format human
验证清单:
- [ ] 所有新测试通过且覆盖率达100%
- [ ] 调试日志中无新的 governor limit 警告
- [ ] 现有功能保持不变
- [ ] 已规划生产环境部署及回滚策略Troubleshooting
故障排除
Issue: Tests fail with "System.LimitException: Too many DML statements"
- Solution: Ensure handler methods collect DML operations and execute outside loops
Issue: Code coverage below 100%
- Solution: Add negative test cases and verify all conditional branches are tested
Issue: Behavior differs from original trigger
- Solution: Review Trigger context variables (new, old, oldMap) are passed correctly to handler
问题:测试失败,提示"System.LimitException: Too many DML statements"
- 解决方案:确保处理器方法在循环外收集并执行DML操作
问题:代码覆盖率低于100%
- 解决方案:添加反向测试用例,确保所有条件分支都被测试覆盖
问题:行为与原触发器不一致
- 解决方案:检查Trigger上下文变量(new、old、oldMap)是否正确传递给处理器
Next Steps
后续步骤
After successful refactoring:
- Document the new handler pattern in your team's wiki
- Update code review checklist to enforce handler patterns for new triggers
- Identify other legacy triggers for refactoring using this skill
- Consider implementing a trigger framework if managing many triggers
成功重构后:
- 在团队Wiki中记录新的处理器模式
- 更新代码审查清单,强制要求新触发器使用处理器模式
- 使用此技能识别其他需要重构的遗留触发器
- 若管理大量触发器,可考虑实现触发器框架