Skip to content

Trigger Name: UserChangeEventTrigger

Last Updated: 2025-10-22 Source Code: https://github.com/AANP-IT/I2C.Salesforce.Metadata/blob/STAGING/force-app/main/default/triggers/UserChangeEventTrigger.trigger

API Name: UserChangeEventTrigger Object: UserChangeEvent (Change Data Capture Event) Pattern: Handler Pattern with Static Method

Business Purpose

This trigger uses Change Data Capture to monitor User email changes and automatically synchronize email updates to the Username field and related Person Account email addresses. Since User triggers have limitations (mixed DML), CDC provides async processing to update both User.Username and Account.PersonEmail fields when User.Email changes.

Trigger Events

This trigger fires on the following events: - ☐ Before Insert - ☐ Before Update - ☐ Before Delete - ☑️ After Insert (CDC events only support after insert) - ☐ After Update - ☐ After Delete - ☐ After Undelete

Trigger Handler

Handler Class: UserChangeEventTriggerHandler.cls Pattern: Static Handler Method Entry Point: Static method call from trigger

trigger UserChangeEventTrigger on UserChangeEvent (after insert) {
    UserChangeEventTriggerHandler.handleAfterInsert(Trigger.New);
}

Process Flow by Event

After Insert

Purpose: Synchronize email changes from User to Username and Person Account

Business Logic: 1. Extract Email values from UserChangeEvent records 2. Get User IDs from change event headers (multiple IDs possible per event) 3. Create User update records with new Username (set to Email value) 4. Query User records to get AccountId values 5. Filter for portal users only (non-portal users removed from update list) 6. Update User.Username records (with allOrNone=false for error handling) 7. Call UserTriggerHandler.updateAccountEmailsAsync() to update Person Accounts

Methods Called: - UserChangeEventTriggerHandler.handleAfterInsert(List<UserChangeEvent> uces) - UserTriggerHandler.updateAccountEmailsAsync(Map<Id, String> accountsToUpdate, Boolean byPassUpdate)

Field Updates: - User.Username: Set to new Email value (for portal users) - Account.PersonEmail: Updated via future method

Related Records Updated: - User records (Username field) - Account records (PersonEmail field, via future method)


Key Business Rules

Validation Rules

  • Only processes UserChangeEvent records with Email field changes
  • Only updates Username for portal users (User.AccountId != null)
  • Username set equal to Email value
  • Account.PersonEmail synchronized asynchronously

Field Updates

  • User.Username: Set to User.Email
  • Account.PersonEmail: Set to User.Email (via future method)
  • User records updated synchronously
  • Account records updated asynchronously via @future method
  • Mixed DML handled via CDC async processing

Bulkification & Governor Limits

Bulkified: Yes (CDC) Max Records Handled: CDC event batching SOQL Queries: 1 (query Users for AccountId) DML Operations: 1 (Database.update Users with allOrNone=false)

Bulkification Strategy

  • CDC batches multiple User changes into single event
  • Single change event can contain multiple User IDs
  • Single SOQL query for all User records
  • Single DML operation with Database.update
  • Future method handles Account updates separately

Governor Limit Considerations

  • CDC Limits: Subject to org CDC allocations
  • SOQL: 1 query per event batch
  • DML: 1 DML statement (with allOrNone=false)
  • Future Method: 1 future method call per execution

Recursion Prevention

Strategy: CDC events cannot trigger themselves + Future method isolation

Implementation: - CDC events fire after committed transactions - Future method runs in separate transaction - Natural recursion protection via CDC and async processing

Scenarios: - No recursion risk with CDC events - Username update doesn't create new CDC events in same transaction - Future method isolation prevents User->Account->User loops

Execution Order & Dependencies

Order of Execution Impact

  • CDC events fire after source transaction commits
  • User.Username updated synchronously in CDC transaction
  • Account.PersonEmail updated asynchronously in future method

Dependent Triggers

  • UserTrigger: Direct trigger on User object (may interact)
  • AccountTrigger: Could be impacted by Person Account updates

Dependent Processes

  • UserTriggerHandler.updateAccountEmailsAsync: Future method for Account updates
  • Person Account management
  • Email synchronization across objects

Error Handling

Strategy: Database.update with allOrNone=false + error logging

User Experience: - Success: Email change captured, Username and Account updated - Event Delivery Failure: Salesforce automatically retries - User Update Error: Logged to debug logs, other records still processed - Account Update Error: Handled in future method

Logging: - Error logging in handler using System.debug - Database.SaveResult errors logged for each failed User update - CDC built-in monitoring

Rollback Behavior: - Partial success allowed (allOrNone=false) - Failed User updates logged but don't stop processing - Future method failures isolated

Dependencies

Apex Classes

  • UserChangeEventTriggerHandler: Handler with static method
  • UserTriggerHandler: Contains updateAccountEmailsAsync future method

Salesforce Objects

  • UserChangeEvent: CDC event object
  • User: Standard object (Username field updated)
  • Account: Standard object (PersonEmail updated)

Custom Settings/Metadata

  • CDC entity settings (Salesforce managed)

External Systems

  • None directly

Testing

Test Class: UserChangeEventTriggerHandlerTest.cls (likely) Coverage: Unknown

Test Scenarios: - Single User email change - Bulk User email changes - Portal vs non-portal users - Users with and without AccountId - CDC event delivery testing - Future method execution - Error handling with invalid email formats

Performance Considerations

Average Execution Time: Fast (CDC routing) Max Records Processed: CDC batch sizes Async Processing: Yes - CDC + Future method

Optimization Opportunities: - Already optimized with CDC and future method - Monitor future method queue - Consider Queueable instead of future for better monitoring

Changes & History

  • Author: Original implementation (team maintained)
  • Pattern: CDC with future method for mixed DML handling
  • Rationale: User object has mixed DML limitations, CDC + future method solves this

Pre-Go-Live Concerns

CRITICAL - Fix Before Go-Live

  • CDC Configuration: Verify User CDC properly enabled with Email field
  • Username Validation: No validation that Email is valid Username format
  • Duplicate Username Risk: Could create duplicate Username if Email not unique
  • Mixed DML: Verify future method properly handles mixed DML scenarios

HIGH - Address Soon After Go-Live

  • Error Handling: System.debug errors not persisted, need custom object logging
  • Future Method Limits: Only 50 future methods per transaction
  • Account Update Bypass: byPassUpdate parameter always passed as false, may need true scenario
  • Non-Portal User Handling: Users removed from update silently, should log

MEDIUM - Future Enhancement

  • Replace Future with Queueable: Better monitoring and chaining capability
  • Event Replay: Document CDC replay procedures
  • Trigger Settings: Custom metadata to enable/disable
  • Username Validation: Add validation before Username update

LOW - Monitor

  • CDC Limits: Monitor org-wide CDC allocations
  • Future Method Queue: Track future method execution times
  • Username Conflicts: Monitor for duplicate Username errors

Maintenance Notes

Complexity: Medium-High Recommended Review Schedule: Quarterly

Key Maintainer Notes: - Uses CDC because User triggers have mixed DML limitations - Username always set equal to Email (no transformation) - Only portal users (with AccountId) get Username updates - Account.PersonEmail updated via future method (mixed DML workaround) - Database.update with allOrNone=false allows partial success - Errors logged but not persisted (System.debug only) - Always test with portal and non-portal users - CDC events can batch multiple User changes

Deactivation Instructions: Add custom metadata check:

trigger UserChangeEventTrigger on UserChangeEvent (after insert) {
    if (TriggerSettings__mdt.getInstance('UserChangeEvent')?.Disabled__c == true) {
        return;
    }
    UserChangeEventTriggerHandler.handleAfterInsert(Trigger.New);
}

CDC Considerations: - User CDC must be enabled in Setup - Select Email field for CDC tracking - CDC events have 3-day retention - Test thoroughly with portal users in sandbox - Monitor CDC allocations (varies by edition)