In these talks by Nicholas Zakas and Addy Osmani they discuss the idea of using the facade pattern as a sandbox when building large scale Javascript applications, in order to decouple the application from the underlying base libraries.
This decoupling would in theory allow you to switch out a base library without needing to rewrite your application modules. However in practice this seems to be more difficult to implement.
There are concrete implementations of this proposed architecture, such as AuraJS. However from looking at the source it seems that the sandbox still has leaky abstractions by returning jQuery objects from some of its methods.
I'm not concerned with AuraJS specifically, but more the general concept of trying to abstract a library like jQuery without losing so much functionality.
As an example, say my facade/sandbox has a dom method .find(selector). I can think of 3 options for what it might return:
A jQuery object - This would leak jQuery out into the consuming modules.
A raw dom element - Loss of functionality, nobody really wants to work with this! No chaining.
A custom jQuery-like wrapper - Could be quite complex, but seems like the ideal solution.
So my question is, how would you abstract a library like jQuery without losing too much functionality, such that it could be replaced at some point in the future with minimal effort?
Here's a very simple example of using modules as an architecture:
<!DOCTYPE html>
<title>Module play</title>
<body>
<script>
// myCore provides all functionality required by modules
// Could use a library in here
var myCore = {
getContainer: function() {
// code in here to find a suitable container in which to put widgets
// This is where different client capabilities will be tested to ensure the
// widget behaves in it's user agent context - desktop, phone, tablet, pad, etc.
// very simple shortcut
return {
element: document.body,
// This function could use a general purpose library
add: function(widget) {
this.element.appendChild(widget.getElement());
}
};
},
// This function could use a general purpose library
getNewWidget: function() {
var element = document.createElement('div');
return {
getElement: function() {
return element;
},
display: function(text) {
// Tightly couple to itself or not?
this.getElement().innerHTML = '<em>' + text + '</em>';
// or
element.innerHTML = '<em>' + text + '</em>';
}
}
}
};
// Missing sandbox layer...
// Add a module - only uses myCore API (access should be controlled by
// the sandbox), does not deal with underlying library or host objects
(function() {
// Get a container to add a widget too
var container = myCore.getContainer();
// Create a widget
var widget = myCore.getNewWidget();
// Add the widget to the container
container.add(widget);
// Give something to the widget to display
widget.display('Hello World');
}());
</script>
</body>
So you can see that at the module level, you don't care about the host environment or underlying library, you are just writing plain ECMAScript. You can get really defensive and do stuff like:
(function() {
var container, widget;
if (!myCore) return;
if (myCore.getContainer) { // Some would include an isCallable test too
container = myCore.getContainer();
}
// getWidget could be a method of container instead so that
// everything you need is either a method or property of container
// or widget
if (myCore.getWidget) {
widget = myCore.getWidget();
}
...
}
and so on so everything is tested and checked. I've left out error handling, but hopefully the example is sufficient.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With