Hoy he estado leyendo ojeando una de las presentaciones de Nicholas Zakas, concretamente la de «Scalable Javascript Applications Architecture«. Aunque no es lo primero que leo sobre el tema, me ha gustado la claridad con la que Nicholas lo explica. Voy a intentar dejar constancia de las impresiones sacadas por si a alguien le puede ayudar.
1) Teoría de módulos
La teoría de módulos (como Nicholas la llama) nos dice que una forma de crear una aplicación que sea escalable y fácil de mantener, esta debe estar compuesta por módulos.
¿Que son módulos?
Un módulo (en nuestros términos) es aquella aplicación independiente que forma parte de una estructura mayor. Cada uno de ellos tiene un trabajo y únicamente se encarga de hacerlo.

(Ver Imagen)
(Me ha encantado esta imagen, da una imagen clara del significado de módulo).
Código
Core.register("hola-mundo", function(sandbox){
// Variables privadas
var priv = "Privada";
// Métodos públicos
return {
init: function(){
try{
sandbox.console("iniciamos el módulo");
} catch(ex) {
alert("No se ha encontrado sandbox");
}
},
destroy: function(){
// destructor
}
};
});
Aquí vemos un ejemplo de módulo que registramos con Core.register()
que lo veremos más adelante.
Si analizamos el módulo vemos que el primer parámetro informa el nombre del módulo y el segundo especifica la funcionalidad del módulo. Esta funcionalidad recibe como parámetro sandbox
(tambien lo veremos más adelante).
Esta funcionalidad nos devolverá un objeto con al menos dos métodos (init()
y destroy()
) que compondrán las opciones disponibles en el módulo.
init()
será tratado como el constructor del módulo y se ejecutará en el momento que se genere el módulo. Por otro lado destroy()
será tratado como el destructor del módulo ejecutándose cuando el módulo sea eliminado.
Sandbox
En desarrollo de software se llama Sandbox al entorno aislado de pruebas donde ejecutan los experimientos sobre las aplicaciones. En este caso, prácticamente se basa de lo mismo ya que cada uno de los módulos usará un entorno aislado para los demás módulos.
Código
var Sandbox = function() {
var private = "Privado";
return {
alert: function(str){
alert(str + private);
},
console: function(str) {
console.log(str);
}
}
};
Como vemos el Sandbox, se trata de un elemento que nos ofrece una serie de métodos con los que podremos interactuar con la aplicación. Por ejemplo, podría emplearse para interactuar con el DOM de la aplicación mediante un framework JS (o no), se podría implementar una versión sencilla y controlada de realizar peticiones Ajax o validar formularios,…. Todo ello disponible desde los módulos.
De esta forma los módulos únicamente deben preocuparse de lo que el Sandbox les permite sin preocuparse de lo que hay por debajo de él, facilitando así el desarrollo y la funcionalidad de cada uno de los módulos.
Ejemplo:
// Añadimos método notify() al Sandbox
var Sandbox = function() {
...
notify: function(opt){
// Comprobaciones previas
// ....
$("#notify #title").text(opt.title);
$("#notify #content").html(opt.content);
}
...
};
// Usamos el método notify() de Sandbox desde el módulo.
Core.register("hola-mundo", function(sandbox){
// Variables privadas
ver ver = "1.0";
return {
init: function(){
try{
sandbox.notify({
title: "Hola Mundo v." + ver,
content: 'Esto es una <strong>Prueba</strong>.<br />Para mostrar el uso de módulos'
});
} catch(ex) {
alert("No se ha encontrado sandbox");
}
},
destroy: function(){
// destructor
}
};
});
En el ejemplo vemos que al iniciar el nuestro módulo, llamaremos al método notify()
de sandbox sin preocuparnos de lo que hará este método.
El método tras una serie de validaciones prévias, simplemente añadirá un título y un contenido a un elemento #notify
usando jQuery.
De esta forma, el Sandbox hace de filtro entre el módulo y el framework controlando lo que este pretende hacer sobre nuestra aplicación.
El Core
En todo lo anterior hemos visto una variable (Core
) que parece que es la piedra angular de todo esto. Se trata de un pequeño script que nos permite registrar módulos y asociar una nueva instancia del SandBox a cada uno de ellos cada vez que se ejecuta.
var Core = function(){
// Variable privadas
var modules = {};
// Cremos la instancia
function createInstance(moduleID){
var instance = modules[moduleID].creator(new Sandbox(this)),
name, method;
if (!debug) {
for (name in instance){
method = instance[name];
if (typeof method == "function") {
instance[name] = function(name, method) {
return function(){
try { return method.apply(this, arguments); }
catch(ex) { log(1, name + "(): " + ex.message); }
}
}(name, method);
}
}
}
return instance;
}
// Método públicos
return {
register: function(moduleID, creator) {
modules[moduleID] = {
creator: creator,
instance: null
};
},
start: function(moduleID) {
modules[moduleID].instance = createInstance(moduleID);
modules[moduleID].instance.init();
},
stop: function(moduleID){
var data = modules[moduleID];
if (data.instance) {
data.instance.destroy();
data.instance = null;
}
},
startAll: function(){
for (var moduleID in modules) {
if (modules.hasOwnProperty(moduleID)) {
this.start(moduleID);
}
}
},
stopAll: function() {
for (var moduleID in modules) {
if (modules.hasOwnProperty(moduleID)) {
this.stop(moduleID);
}
}
}
};
}();
Como vemos se trata de un pequeño script (esto es una base, se puede extender dependiendo de las necesidades del proyecto) que nos permite generar los módulos que queremos por cada aplicación.
Ejemplo
Core.register("...", function(sandbox){});
Core.register("...", function(sandbox){});
Core.register("...", function(sandbox){});
Core.register("...", function(sandbox){});
...
// Iniciamos todos módulos
Core.startAll();
Código completo
Bueno, para verlo todo junto, he creado un fichero .js donde podrás verlo más claramente.