1

BACKGROUND

I need to display some data from another system on a lightning component. Let's say I get a JSON string from the external system as following:

{
  "initialStatus": "Active",
  "name": "Test Data"
}

I have a service class that only responsible for getting data from that system.

public class ExternalService
{
    public ExternalServiceData getData() {...}

    public class ExternalServiceData
    {
        public String name { get;set; }
        public String initialStatus { get;set; }
    }
}

And apex controller for aura component uses that class.

public with sharing class AuraController
{
    @AuraEnabled(cacheable=true)
    public static String fetchExternalServiceData()
    {
        try {
            ExternalService.ExternalServiceData externalServiceData  =
                new ExternalService().getData();
            return JSON.serialize(externalServiceData);
        } catch (Exception ex) {
            throw new AuraHandledException(ex.getMessage());
        }
    }
}

Now my aura component uses externalServiceData returned by controller in the JSON format.

QUESTION

Now everything works just fine, but I am concerned about maintainability of such architecture. With this approach my UI depends on the ExternalService.ExternalServiceData data structure. If something in ExternalService.ExternalServiceData changes, let's say name changes to some_name, I would have to change it in every component where it is used.

So how is this generally solved?

I have an idea to keep the same wrapper/model on the controller class and then convert it before returning results back to aura component, but it feels like writing duplicated code and boilerplate as a result.

closed as primarily opinion-based by Sebastian Kessel, battery.cord, glls, Vijay Ganji, Himanshu Aug 21 at 1:29

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.

3

Well, software api architecture is built on contract, if the contract changes then you have to make changes to your implementation.

Yes, its there, it's inevitable. SF too changes the contract, but the best thing it does it maintains backward compatibility(v40, v41 etc)

Lets assume, the 3rd party API is on version 1, thus it cant change its contract on version1. They can create new contract for new version, but for a few months/years have to support the old version. They won't add features to old versions, just to the new version, so that you have to change your implementation to use newer features.(VF to Lightning)

But there would be very rare API that don't follow best practices and thinks about backward compatibility.

In that case, you can use Sebastian's answer. You can even take it to the next level. Maintain the field mapping 1-1 in Custom Settings / Metadata, and thus if it changes, you can just change the value of the configs and make your app work.

  • 3
    +1 for the awesome suggestion of storing mapping info in Settings or Custom Metadata – Sebastian Kessel Aug 19 at 18:58
2

If you're concerned about the source of the data changing on you (because you don't control it) then yes, you may have to use a wrapper to ensure that you control all the variable/property names.

It is duplicated code, but if you plan to use the JSON payload in several places, it will pay off to abstract it.

I have a lot of wrapper classes that look like this (they are usually contained inside an outer class called Messages or similar) (I did not add any error-handling here, but you get the drift)

public class XXXWrapper { 

   @AuraEnabled
   public String prop1 {get;set;}
   @AuraEnabled
   public String prop2 {get;set;}


   public XXXWrapper(String jsonData) {
      //Deserialize using deserializeUntyped
      Map<String, Object> m = (Map<String, Object>) JSON.deserializeUntyped(jsonData);

      //Populate your props
      this.prop1 = (String) m.get('PropName');
     ....
   }
}

You can then send this class up to Aura if you want, and consume it directly.

Not the answer you're looking for? Browse other questions tagged or ask your own question.