Making Apex More Dynamic

by Sam Forbes - November 08, 2024
Making Apex More Dynamic

Apex Code can feel very rigid once it’s in production. If you want to change any logic inside a class, you’ll need to go through an entire sandbox-to-release cycle – even if the changes are minor. I want to discuss a technique to address this inflexibility that I’ve rarely seen mentioned online or at events: dynamically creating Apex class instances based on JSON.

The Scenario

When writing an Apex class, there’s regularly a piece of that code that we can identify as likely to change. For example, let’s say you have a trigger on Opportunity that sets a checkbox field ‘Big_Deal__c’ to true when the Opportunity amount is greater than £50k. Businesses grow over time and £50k isn’t always going to constitute a big deal. If this is hard-coded into an Apex class, your code will need to change as this business scales. I’m going to walk you through a solution that removes the need for those code changes.

How It Works

This technique relies on Apex’s ability to deserialize a JSON string into an instance of an Apex class. If the JSON string you are deserializing has a key that matches the name of a member variable in the target class, that member variable will be set to the key’s value. This will make more sense when we go through an example.

Below is an example class. There are a couple of things to highlight for you:

  • The JSONAccess decorator. This gives access to deserialize a JSON string into an instance of this class. Without this, the magic won’t happen.
  • The two private member variables: ’value’ and ‘numberValue’.

We’re now going to run the below piece of anonymous Apex. Some things to note:

  • We’re providing two parameters to the deserialize method:
    • A JSON string with keys that match the names of the member variables in the DeserializeIntoMe class: value & numberValue
    • The Type that we want the JSON string to be deserialized into. 
  • We then call DeserializeIntoMe’s print method to debug the member variable values.

When we run this, we get the output below.

We’ve just constructed an instance of DeserializeIntoMe with values supplied from JSON. These values aren’t written anywhere in the code but are passed into private class variables.

The values we’ve supplied in this example have been deserialised into quite simple types: Integer and String. This technique also supports Lists, Decimals, Booleans and Maps. It does not support deserialising a value into a generic Object type – you need to be specific.

The Bigger Picture

So where do we get these JSON strings from? How can we apply this to our original scenario? Both are great questions. The answer to the first question is: anywhere you can store text data. We generally use long text area fields on custom metadata or even object records as these provide more than enough space for relatively short JSON strings.

To apply this technique to our original scenario, I’m going to briefly introduce the Nebula Core package and its custom metadata-based trigger framework. The trigger framework relies on two key components:

  1. Having Apex classes that implement interfaces for the trigger context you want them to run in e.g. AfterInsert or BeforeUpdate
  2. A corresponding nebc__Trigger_Handler__mdt custom metadata record for that class to run in that context. These records contain the name of the class and a field to provide JSON parameters to that class if required.

In each stage of the trigger context, the framework uses the custom metadata records to find the name of every trigger handler class that should be running in that context. It then uses the JSON deserialization technique to construct an instance of each class and calls the relevant handler method. Constructing class instances in this way allows us to inject our variable values dynamically.

Applying the Solution

Below is our trigger handler class that implements the BeforeUpdate interface. We’ve marked it as Deserializable and created a Decimal type member variable to hold the threshold amount for a big deal. We’ve had to make this class global as the Nebula Core trigger code is namespaced and could not otherwise be seen by the framework.

The handleBeforeUpdate method is called by the framework in the before-update trigger context. In this method, we’re simply iterating through the Opportunity records and setting our Big_Deal__c checkbox to true if the Opportunity amount is greater or equal to the threshold and false if it’s not.

The last piece is the trigger handler metadata. We’ve referenced the name of the Apex class we’ve created and provided a JSON string that will set the bigDealThreshold to 50,000.

Now when that threshold needs to be changed, the only update that needs to be made is to the parameters field on the custom metadata record. The code doesn’t need to change! Our Apex class has been made dynamic and can meet the demands of a growing business without requiring code changes.

Wrapping Up

One of the key aspects of software engineering is planning for change over time. In a world of seemingly rigid and inflexible code, this can be a challenge. Hopefully, with this technique, you’ve gained another tool in your toolbox to help manage change smoothly. If you’d like to know more about this technique and how Nebula can help manage change in your org, get in touch with one of our consultants here

You can read more about another application of this technique here: Further reading

Related Content


Get In Touch

Whatever the size and sector of your business, we can help you to succeed throughout the customer journey, designing, creating and looking after the right CRM solution for your organisation