Benefits of Creating a Generic Platform Event

Author: Bruce Tollefson Published: March 6, 2022; Modified: May 9, 2022
connected_dots

Platform Events are a very useful feature within Salesforce. As the underlining technology to be able to use event-driven architecture, platform events within Salesforce provide near real-time event messaging which can be used internally and externally. Platform Events like other SObjects are pretty flat, the structure is an object with key-value pairs. In most scenarios that works well, however platform events serve a different purpose than other SObjects and at times may need more flexibility. This article will focus on how to add additional flexibility to platform events through the use of a generic platform event.

The fundamentals of this event is relatively straight forward where we will create an event with a long text field. Within that event we can generate any JSON and publish the event, providing a dynamic way of sending values as platform events. First, we need to set up the event and take a look at the structure of the event.

Create Generic Platform Event

Go to Setup –> ‘Platform Events’ –> New Platform Event –> Type in ‘Generic Event’ once saved. You have just created the platform event object now click ‘New’ under the Custom Fields and Relationships section –> Text Area (Long) –> Label ‘Generic Payload Text 1’ and length ‘131,072’.

Generic Platform Event Creation

To view the generic Salesforce platform event you can create a Lightning Web Component to subscribe to the event channel ‘/event/Generic_Event__e’ following this article. Then while subscribing to the event execute the following in anonymous apex:

Generic_Event__e genericEvent = new Generic_Event__e(Generic_Payload_Text_1__c='Testing generic event'); EventBus.publish(genericEvent);

After creating the event we can now work on creating an event that is dynamic with additional structure and more flexibility than a flat SObject like platform event.

Adding JSON to the Generic Platform Event

Let’s say as an example you needed to send an opportunity event with the list of all OpportunityLineItems that are associated to the opportunity. Since there is no list in the custom field data type if you need to send a list there are two options. First, create multiple fields with a numeric indicator at the end to identify the opportunity line item field and how many items there are, this can very quickly get out of hand and lead to many custom fields being created. Or second, use the Generic_Payload_Text_1__c field to insert a serialized list in the event. As an example in an org with several OpportunityLineItems run the following in anonymous apex:

List<OpportunityLineItem> oliList = [Select Id, Name from OpportunityLineItem]; Generic_Event__e genericEvent = new Generic_Event__e(Generic_Payload_Text_1__c=JSON.serialize(oliList)); EventBus.publish(genericEvent);

If you run the above code you will notice the list is converted to a string as an array with ‘[‘ and end with ‘]’ if the parser is having issues and requires the JSON to be an object instead of an array the JSON generator class can be used:

List<OpportunityLineItem> oliList = [Select Id, Name from OpportunityLineItem]; JSONGenerator gen = JSON.createGenerator(false); gen.writeStartObject(); gen.writeObjectField('OLIList',oliList); gen.writeEndObject(); Generic_Event__e genericEvent = new Generic_Event__e(Generic_Payload_Text_1__c=gen.getAsString()); EventBus.publish(genericEvent);

As you can see the field Generic_Payload_Text_1__c has the number 1 at the end. The long text field has a character limit of 131072 characters. This is a very long length however if need be additional fields could be created and a method to check the length of the string and split it into multiple strings where the subscriber could then concatenate the fields together.

Additional fields can be used to separate different types of events. The generic platform event can also be used as a wrapper to augment a Change Data Capture event, subscribing to this event instead of directly subscribing to the CDC event.

Augmenting Change Data Capture Events

Let’s say you need to publish opportunity changes, however, you don’t want any external systems to use the Salesforce OwnerId as it is only stored in Salesforce. Instead, you would like to send an external enterprise Id. Depending on how the CDC event occurred in the event the OwnerId may be added, this field is not writeable and the event can not be updated to add fields using the put() method. To accomplish this we could create a wrapper class with the event and an External Id field or add it to the platform event. Seeing as how this field could more than likely be used with this event elsewhere we will add it to the generic platform event. First create a new text field on the Generic_Event__e, with an API name of External_Owner_Id__c. If the user field doesn’t have an external Id field do the same. Then add the following as an OpportunityChangeEvent trigger:

trigger OpportunityChangeEventTrigger on OpportunityChangeEvent (after insert) { List<Generic_Event__e> geList = new List<Generic_Event__e>(); Set<Id> OpportunityIdSet = new Set<Id>(); for(OpportunityChangeEvent event :Trigger.New){ EventBus.ChangeEventHeader header = event.ChangeEventHeader; checkRecordIds(header.recordIds);//headers could have multiple recordIds } Map<Id, Opportunity> ownerMap = new Map<Id, Opportunity>([Select Id, Owner.External_Id__c from Opportunity]); for(OpportunityChangeEvent event :Trigger.New){ EventBus.ChangeEventHeader header = event.ChangeEventHeader; for(String recordId :header.recordIds){//there is potential for multiple messages to be sent with the same record Id and the owner may be different so they all will need to be separated Generic_Event__e ge = new Generic_Event__e(Generic_Payload_Text_1__c=JSON.serialize(event,true),//serialize event to string External_Owner_Id__c= ownerMap.get(recordId).Owner.External_Id__c);//add the external Id to the event geList.add(ge);//add to list for publish } } pubishEvents(geList); private static void checkRecordIds(List<String> recordIdList){ for(String headerId :recordIdList){ try{ Id recordId = (Id)headerId; OpportunityIdSet.add(recordId); }catch(Exception e){ //Watch out for wildcard ids: https://developer.salesforce.com/docs/atlas.en-us.change_data_capture.meta/change_data_capture/cdc_event_fields_header.htm //do something with the events that don't have full headers } } } private static void pubishEvents(List<Generic_Event__e> genericEventList){ List<Database.SaveResult> results = EventBus.publish(genericEventList); for(Database.SaveResult sr : results){ if(!sr.isSuccess()){ //do something with unsuccessful publishes } } } }

Of course, this is not best practice as all logic is in the trigger but more so demonstrates how this can be achieved.

The change data trigger operation could have multiple events and multiple record Ids within each event. Each record Id could have a different owner. First, loop through and get all of the record Ids and place them in a set, checking to see if there are any record Ids that aren’t an Id. Then query the Opportunity object and get a set of the record Id and the owner external Id. Next loop through the events and the list of records Ids to create a generic event with the CDC message serialized and the External_Owner_Id__c field set (assuming each user has an external Id).

GitHub repo.

One comment on “Benefits of Creating a Generic Platform Event”

  • Mark says:

    Thanks for your blog, nice to read. Do not stop.

  • Leave a Reply

    Your email address will not be published. Required fields are marked *