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.
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));
}
}
});
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);
}