Can anyone suggest a pattern that can be used for writing a JavaScript API wrapper, where there is no shared code between multiple implementations? The idea is to provide the client consumer with a single wrapping API for one of many possible APIs determined at runtime. APIs calls could be to objects/libraries already in the app environment, or web service calls.
The following bits of pseudo-code are two approaches I’ve considered:
Monolithic Solution
var apiWrapper = {
init: function() {
// *runtime* context of which API to call
this.context = App.getContext();
},
getName: function() {
switch(context) {
case a:
return a.getDeviceName() // real api call
case b:
return b.deviceName // real api call
etc...
}
// More methods ...
}
}
Pros: Can maintain a consistent API for the library consumers.
Cons: Will result in a huge monolithic library, difficult to maintain.
Module Solution
init.js
// set apiWrapper to the correct implementation determined at runtime
require([App.getContext()], function(api) {
var apiWrapper = api;
});
module_a.js
// Implementation for API A
define(function() {
var module = {
getName: function() {
return deviceA.getDeviceName();
}
};
return module;
});
module_b.js
// Implementation for API B
define(function() {
var module = {
getName: function() {
// could also potentially be a web service call
return deviceB.getName;
}
};
return module;
});
Pros: More maintainable.
Cons: Developers need to take care that API remains consistent. Not particularly DRY.
This would be a case where something along the lines of an Interface would be useful, but as far as I’m aware there’s no way to enforce a contract in Js.
Is there a best practice approach for this kind of problem?
Second approach is better because it is modular and you or a third person can easily extend it to incorporate other services. Point of “API remaining consistent” is not so valid because proper unit-tests you keep things consistent.
Second approach is also future proof because you don’t know what unimaginable things you may have to do to implement say
getNamefor service C, in that case it is better to have a separate module_c.js with all complications instead of spaghetti code in monolithic single module.Need for real-interface IMO is not so important, a documented interface with unit-tests is enough.