Breaking the Synchronous Chain: Using Salesforce Platform Events for Decoupled Logic
Stop hitting CPU limits in Salesforce. Learn how to use Platform Events to decouple heavy business logic from your primary record transactions for better performance and reliability.
18 Jun 2026, 03:59 UTC

The Governor Limit Wall
\nYou have a complex business process: when an Opportunity is marked 'Closed Won,' you need to generate an invoice, notify a third-party ERP, update a legacy reporting table, and send a welcome email. If you put all of this in a synchronous Apex trigger, you risk hitting CPU time limits or DML governor limits. Worse, if the ERP API call fails or hangs, the user sees a generic 'Apex CPU time limit exceeded' error, and the Opportunity record fails to save entirely.
\nThe solution is to move from a synchronous Apex trigger to an event-driven model using Platform Events. Instead of executing the logic during the save operation, the trigger simply publishes a \"message\" to the event bus and finishes. The heavy lifting happens in a separate transaction.
\nHow Platform Events Decouple Transactions
\nPlatform Events act as a publish-subscribe (pub-sub) mechanism. A publisher sends an event to the Salesforce event bus, and any number of subscribers (Apex triggers, Salesforce Flows, or external systems via CometD) can listen for and act on that event.
\nThe critical advantage here is the transaction boundary. When a Platform Event trigger executes, it runs in its own separate transaction. This means the original user who updated the Opportunity is no longer waiting for the ERP call to finish; their record is saved, and the UI unlocks immediately.
\nChoosing Your Publish Behavior
\nWhen defining a Platform Event, you must choose between two publishing behaviors. This decision determines how your system handles failures:
\n- Publish After Commit: The event is only sent to the bus if the original transaction succeeds. This is the safest bet for most business logic, ensuring you don't trigger a welcome email for an Opportunity that failed to save due to a validation rule.
- Publish Immediately: The event is sent regardless of whether the main transaction commits or rolls back. This is useful for auditing or logging errors where you need a record of the attempt even if the save failed.
Worked Example: The Order Integration Pattern
\nImagine a scenario where an Order__c record creation must trigger an external shipment request. Instead of a synchronous callout, we use a Platform Event called Shipment_Request__e.
1. Define the Event
\nCreate a Platform Event Shipment_Request__e with a custom field Order_Number__c (Text).
2. Publish the Event (Apex Trigger)
\nRun this in a trigger on the Order__c object. Note that we are only performing a simple insert of the event, which is extremely lightweight.
trigger OrderTrigger on Order__c (after insert) {\n List<Shipment_Request__e> events = new List<Shipment_Request__e>();\n for (Order__c ord : Trigger.new) {\n events.add(new Shipment_Request__e(Order_Number__c = ord.OrderNumber));\n }\n // This publishes the event to the bus\n EventBus.publish(events);\n}\n3. Consume the Event (Subscriber Trigger)
\nCreate a trigger on the Shipment_Request__e object. This runs asynchronously.
trigger ShipmentRequestTrigger on Shipment_Request__e (after insert) {\n for (Shipment_Request__e event : Trigger.new) {\n // Call the external ERP API here\n ERP_Integration_Service.sendRequest(event.Order_Number__c);\n }\n}\nCritical Limitations and Risks
\nDecoupling isn't free. There are three primary constraints to manage:
\n- Daily Limits: Salesforce imposes limits on the number of events published and delivered per 24 hours based on your edition. High-volume systems must monitor these via the
/services/data/vXX.X/limitsAPI. - No Direct Record Access: The event trigger does not have the original record in its
Trigger.newcontext. It only has the data you explicitly put into the event fields. To update the original record, you must perform a separate SOQL query using an ID passed in the event. - Recursive Loops: If a Platform Event trigger updates a record, and that record update publishes the same Platform Event, you will create an infinite loop that will rapidly exhaust your daily limits. Always implement a check (such as a static variable or a specific flag) to prevent re-publishing.
Verification Strategy
\nTo verify the event is firing without writing a full consumer, use Workbench. Navigate to Streaming Push Topics and subscribe to /event/Shipment_Request__e. Perform the action in Salesforce and observe the JSON payload appearing in the Workbench console in real-time.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.