Happy Business Starts Here

Re: How We Implemented a Custom Event Framework

Zuora Alumni

As the market leader in the recrurring customer and billing management space, Zuora's Central Platform is expected to provide rich communication capabilities. For example, sending an email to our customers when a payment method is updated, or firing callouts that trigger downstream integration endpoints as billing events take place.


All of these communications are based on business events, which are business-relevant changes for our customers on the Zuora platform.  Today, Zuora supports 24 predefined events limited to Billing, Payment and Finance domains; but our customers are asking for more. A lot more!


The Existing Event Framework


The design of Zuora’s event framework, conceived 6+ years ago, doesn't make it easy to add new business events; given the somewhat archaic nature of the framework, adding a new event isn’t a trivial operation, requiring new coding, and pulling in valuable resources from more important product roadmap work.  Having identified some of the limitations with the existing framework, we decided to build a more modern, scalable framework which allows customers to define custom business events for themselves.


The Solution


To solve the problem of an old and non robust event framework, we defined a flexible expression language to define event triggering conditions against the Zuora Object Model.  The model, shown in Figure 1, should be familiar to our customers.  When an object changes, conditions defined on the object are evaluated against the change, and a business event is triggered if one of the conditions is met.



Figure 1 - Zuora Object Relationship


Expression Language Examples


For example, a triggering condition defined on an Invoice object may look like this:



changeType == ‘UPDATE’ && Invoice.Status == ‘Posted’ && Invoice.Status_old != ‘Posted’ && Invoice.Amount > 1000


The condition is to trigger an event when an invoice is posted with amount over $1000. The expression itself is a JEXL expression, but we customized it by adding conventions and keywords to adapt to our domain use cases, for example:


  • changeType is a keyword to specify what kind of change happened to the object. Its acceptable values include INSERT, UPDATE and DELETE
  • Invoice.Status refers to field Status of Zuora object Invoice
  • Variables with _old suffix represent a previous value of the corresponding object field

Moreover, JEXL built-in functions can be used as well. We can even define conditions on custom fields, which is very hard to implement in hard-coded manner because custom fields are customer/tenant specific. Here’s one real example:


(changeType == 'UPDATE' || changeType == ‘INSERT’) && size(Account.CrmId) != 18 && size(Account.TradingPartnerCode__c) > 0

TradingPartnerCode__c is a custom field defined by a Zuora customer for use in their tenant.

Micro-Service Based Design


Beneath the surface, there are several services working closely to support this new approach to triggering business events. The various components are shown in Figure 2, below. A glossary follows describing the components represented in the figure:



Figure 2 - Service Components for the new event framework


CDC (Change Data Capture): Sends changes from the database to the Database Change topic in Kafka. Specifically, this component extracts mysql binlog then packages table row changes into a message.


Kafka: Messaging infrastructure we rely on to publish and subscribe to streams of messages.


Transformer: Consumes Database Change topic and translates the low level row changes into high level business object changes, and then sends them to the Object Change topic.


OCP (Object Change Processor): This component is where the triggering conditions are evaluated. It consumes Object Change messages, triggers out business events based on the mechanism explained above, and sends events to the Business Event topic. Also, REST APIs are provided for customers in order to manage event triggering conditions.


When a business event is triggered, the payload is loaded with all the facts related to the object change, including the snapshot of the current field values, the previous values, the timestamp, and other metadata for debugging purposes. The downstream services, like the Notification service, can then consume messages from the Business Event topic and send out emails to our customers, or fire callouts to customer integration points.




With the simple customized expression language and the above set of services, nearly any change event that takes place on Zuora’s object model can be defined and detected. This new framework is more robust and scalable than the predefined approach but with any new framework, there are enhancements we are looking at adding to the new framework in the future, including support for more Zuora objects, multi object changes, ordering and latency enhancements.

Valued Scholar

How do I implement/access this custom event framework please?  Is it an add on I need to purchase?  Thanks, April

Valued Scholar

Its been another year - is this avilable and how?

Zuora Staff

Yes, it's available. You can ask for the feature by submitting a request at Zuora Global Support.



Based on documentation when "order features" are enabled in my zuora instance there are many limitations of build-in events: https://knowledgecenter.zuora.com/Central_Platform/Notifications/A_Viewing_the_Supported_Event_Types

Especially key-dates events are not supported - which are basic events in subscription business.


How can I create custom event triggers for subscription start/end date, billing period etc? Is it possible to use Date() method to compare subscription state etc? Zuora support couldn't help me with that.


This is quite important, cause now it seems that ZUORA with orders enabled lacks basic functionality.

Savvy Scholar

Hi it seems like we can create the customized event trigger now. It is actually a flexible and powerful design for many integration/interface design. But I have two questions for this wonderful feature:

  • after a quick test, it is interesting to find the event trigger like changeType = 'INSERT" works well, but the changeType = 'UPDATE' works a little bit weird with custom fields' conditions.For example, in the following condition, I expect the update is triggered only when the custom field "external system" is "salesforce", but actually the system behaves that only when the external system custom field is updated from null to "salesforce", as a quick example.... "CONDITION": "CHANGETYPE == 'UPDATE' && PRODUCTRATEPLAN.EXTERNALSYSTEM__C == 'SALESFORCE' && PRODUCTRATEPLAN.NAME != PRODUCTRATEPLAN.NAME_OLD"
  • Another question is, do we have any plan to design the event trigger like changeType = 'DELETE"? similar to Salesforce's trigger like beforeDelete, afterDelete... which would be another powerful subset...

Looking forward to master's reply.