. . .

Best practices

This page contains some common use cases and proven practices when using ApiOmat.

Versioning

Most changes in the API are caused by adding more classes and attributes to a module. ApiOmat itself can handle these changes even if the data is stored in MongoDB. Also, running apps with old SDKs will continue to work, because unknown data is ignored during requests. This implies that even pure REST requests will still work with missing or unknown attributes; these will be ignored by the ApiOmat REST API.

Changes in class or attribute names cannot be managed automatically, because data conversions or deletions in the database cannot be done without manual intervention. Also, existing apps won't work as expected after this kind of breaking changes.

If there are really breaking changes to implement, best practice is to leave the old module as is and create a second one, maybe including the version in its name (e.g. "oldModuleName_v2"). The new module should copy all classes and attributes of the old one. All classes in the new module should be set transient and should contain an implementation that proxies requests for update/delete/... to the old module.

If the old module contained transient classes, copying the business logic to the new module would also be possible (will lead to code duplication).

Caching

ApiOmat can connect to legacy systems using a variety of APIs. Although these non-mobile optimized systems can be made accessible for mobile devices very easily using ApiOmat, usually some actions need a caching mechanism to ensure proper performance on the users' side.

A typical use case would be fetching a large list of detailed results from a legacy system. Imagine the legacy system would only support calls which return a list of reduced details of entities:

List<Customer> getAllCustomersList()

The returned customers will only contain the customers name, but all other attributes (details) of the customer like firstName, lastName, etc will be null. To get these details, a second request is needed which requires the name of a single customer:

Customer getCustomerDetails(String name)

Fetching a list of thousands of customers would usually last several minutes, depending on the legacy system. To speed up this use case, fetching the customer details could be cached in ApiOmat's MongoDB using only a few straightforward lines of code:

public List<Customer> doGetAll( String query, Request r ) {
List<Customer> returnList = new ArrayList<>( );
/* get customers from legacy system */
List<Customer> customers = searchCustomersInLegacy( query );
/* look for details in cache first and fetch from legacy system if not found */
customers.forEach( c -> {
IModel<?> cachedCustomer = this.model.findByForeignId( c.getForeignId( ), "Ordinary", "CustomerCache", r );
if ( cachedCustomer == null ) {
Customer detailCustomer = searchCustomerDetailInLegacy( c.getForeignId( ) );
returnList.add( detailCustomer );
/* save new in cache */
cachedCustomer = convertToCached( detailCustomer );
((CustomerCache)cachedCustomer).save( );
}
else {
returnList.add( convertToOriginal( cachedCustomer ) );
/* maybe check creation date and drop model from cache afterwards... */
}
} );
return returnList;
}

Everything that is needed is an additional non-transient class which will be used for storing the cached data. The example above used two convert methods to transform an object between both representations.

Automatic SDK download

Since every action done in ApiOmat can be invoked via a REST request, this is also true for downloading frontend SDKs in your development environment. Please take a look at this article.

Internationalization / i18N

An internationalization of texts can be realized by using maps. The language code is the key and the text is associated value:

{"language_code1":"lang1_text","language_code2":"lang2_text",....}

The following examples demonstrate this.

Android
final String language_code="de";
final Conference conference = new Conference();
 
Map<String, String> i18n_name = new HashMap<String,String>();
i18n_name.put("de", "Wie benutze ich properties");
i18n_name.put("en", "How to use properties");
conference.setI18n_name(i18n_name);
 
Map<String, String> i18ns_guests = new HashMap<String,String>();
i18ns_guests.put("de", "Gastsprecher: John Doe");
i18ns_guests.put("en", "Guest speaker: John Doe");
conference.setI18n_guests(i18ns_guests);
 
conference.saveAsync(new AOMEmptyCallback() {
@Override
public void isDone(ApiomatRequestException exception) {
//if exception != null an error occurred
if(exception == null) {
Log.i ("Conference",conference.getI18n_name().get(language_code));
Log.i ("Conference",conference.getI18n_guests().get(language_code));
}
}
});
JavaScript
var LANGUAGE="de";
function showConference (conference)
{
console.log(conference.getI18n_name()[LANGUAGE]);
console.log(conference.getI18n_guests()[LANGUAGE]);
}
 
function initConference ()
{
var conference=new Apiomat.Conference();
var i18n_name = {
"de" : "Wie benutze ich properties",
"en" : "Talk about properties"
}
conference.setI18n_name(i18n_name);
var i18ns_guests = {
"de" : "Gastsprecher: John Doe",
"en" : "Guest speaker: John Doe"
}
conference.setI18n_guests(i18ns_guests);
var saveCB = {
onOk: function () {
showConference(conference);
},
onError: function (error) {
console.log("error "+error);
}
}
conference.save(saveCB);
}

* link only available in Enterprise Documentation