Best Practice: dependency on data model namesears322 hmavbspur 8s
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.
-
3worth checking out YAGNI – cropredy 7 hours ago
2 Answers
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.
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.
-
2+1 for the awesome suggestion of storing mapping info in Settings or Custom Metadata – Sebastian Kessel 6 hours ago