La web está cambiando, vaya si lo está haciendo. Todavía recuerdo con claridad mis primeros encuentros con HTML, aquellas “páginas personales” de diseño ridículo y peor contenido que nos sacábamos de la manga, orgullosos, Diego y yo. El súmmum de la técnica era incrustar un GIF animado para darle movimiento al asunto, y enviar datos vía formulario era el no va más de la interactividad. Tiempos entrañables aquellos.
Hoy en día la página más chorra usa todo tipo de artimañas técnicas para ser dinámica, interactiva y rápida, no vayamos a tener que esperar. La idea es, como vamos viendo cada vez más claro, difuminar (si no borrar) el límite entre página web y aplicación nativa. Eso de clickar links y esperar a que cargue una nueva página ha quedado desfasado, esto ha de parecer una aplicación, no un conjunto de documentos enlazados. Ahí tenemos a AJAX, Comet, WebSocket y otro buen puñado de tecnologías y técnicas que tratan de abstraernos del concepto de página web y llevarnos en volandas a la era de la aplicación web. Y todo ello de la mano de JavaScript, el otrora patito feo ahora elevado a lingua franca de esta nueva era y lenguaje del momento.
Y en estas que montamos Ducksboard, y queremos crear una aplicación web de las modernas y mega-interactivas para el frontend. Drag’n'drop, datos actualizados en tiempo real, menús contextuales y modales en cada click, … y todo ello sin cargar jamás una nueva página, que no se note que estamos en un navegador. Aquí estamos a la última, oiga. Hasta ahí todo bien, “el concepto es el concepto” que decía Pazos. Pero ponerlo en práctica es otra cosa, y eso ya os lo digo yo.

¿Pero y el concepto? ¿Eh? ¿¡Eh!? ¡Aaaamiga, a los hechos me repito!
Jan y yo hemos hecho web antes. Yo, de hecho, un montón. Pero lo cierto es que ambos nos manejamos más en el lado servidor. Sabemos de bases de datos, de cachés y de asincronía, olé nosotros. Pero nuestro JavaScript, pues justito. Somos perfectamente capaces de tirar de jQuery y un poco de AJAX para enviar formularios y actualizar el contenido de algún elemento del DOM, sí, pero pronto íbamos a descubrir que para crear algo como Ducksboard, eso iba a quedarse un poco corto…
Pesadilla antes de navidad
(N.d.A: de hecho fue mucho antes de navidad, allá por el verano 2011, pero no conozco títulos de películas sobre pesadillas veraniegas, y esta es muy buena, joder)
Como gente de backend que somos, nos lanzamos con ganas e ilusión a escribir el sistema de recopilación de datos que algún día nos haría famosos y ricos. Que si traigo datos de aquí, que si acepto actualizaciones de allá, que si empujo el resultado por allí. Todo muy bien, muy orgullosos todos. Ahora bien, la idea era ofrecer un dashboard web, así que necesitábamos un frontend web. Además Vostok nos había hecho un diseño de agárrete que vienen curvas, así que con más razón aún. Nada, que no sufra nadie, el frontend iba a ser un momento.
Me monto un Django por aquí, le hago servir un par de templates HTML para dibujar los widgets por allá, y con jQuery y algún que otro selector le doy vida al conjunto. Resultado… ¡una buena mierda! No porque no funcionase, que más o menos lo hacía, sino por lo inmantenible y lento del resultado, a saber:
- Cada vez que se querían editar propiedades de un widget / dashboard, había que ir a Django, renderizar la vista de formulario, y volver al frontend a dibujarlo: lento.
- El código se convirtió enseguida en un bosque de selectores descontrolado. Todos los elementos hacen cosas distintas al ser clickados, arrastrados, eliminados, … y organizar toda esa interactividad tras selectores de jQuery se volvió intratable bien rápido.
- No había estructura de aplicación. Algunas cosas pasaban en Django, otras en el JavaScript (sin orden como he comentado), no había relaciones claras entre ningún componente… Era difícil pensar en términos de aplicación, porque habíamos estado desarrollando una página web a la vieja usanza.
Y así fue amigos como, tras cerca de un par de meses escribiendo el frontend, el dashboard en sí, nos encontramos con algo entre manos que no queríamos ni tocar con un palo. Nuestro principal problema era el enfoque dado al desarrollo. Habíamos enfocado nuestra aplicación como una página web al uso: toda la lógica vive en el servidor y en el cliente sólo hay presentación. Incluso la más pequeña de las acciones implicaba ir a por datos al servidor, lo que ralentizaba mucho el proceso y afectaba a la usabilidad. Y teníamos tanta “magia” de visualización en el cliente que el JavaScript se había vuelto inmantenible. Muy mal asunto, algo había que hacer. La alternativa era dolorosa, porque iba a implicar reescribir mucho código, algo poco aconsejable en un estadio tan inicial de proyecto, y nadie nos decía que fuese a salir mejor. Malos tiempos para la lírica. ¿Cómo saldríamos de aquella?
Algunos hombres buenos
Comentaba hace un rato que nos estamos moviendo a un panorama de aplicación web, más que de página web. ¿Y qué quiero decir con eso? Pues que buena parte de la lógica se está moviendo del servidor al cliente. Evidentemente, mucha lógica no va a poder moverse fuera del servidor, el acceso a la base de datos por ejemplo. Pero mucha otra lógica, como validar modelos, agruparlos o filtrarlos, mostrar contenido basado en datos de esos modelos, pueden empujarse a la capa de cliente usando JavaScript. No en vano, en los últimos meses ha florecido todo un ecosistema de frameworks MVC en JavaScript que ofrecen herramientas para estructurar la aplicación del lado cliente tal como lo haríamos en el servidor: definiendo clases de modelo, vista y controlador.
Al final la idea es sencilla: hacer tanto trabajo como sea posible en el navegador, y sólo acceder al servidor cuando sea totalmente necesario. Y que todo ese código en el navegador esté bien estructurado, claro. Aunque cada framework MVC para JavaScript tiene sus particularidades, la idea principal es la misma: empujar una representación de los modelos del servidor al cliente, como objetos JavaScript, y tomar tantas decisiones como sea posible en base a esa representación. Con eso ganamos en velocidad de respuesta en el frontal, descargamos el servidor y todo se nota fluido y ágil, regocijémonos. ¿Sirve esta aproximación para cualquier proyecto web? Bueno, eso depende. Un framework MVC JavaScript tiene sentido siempre que sea necesario implementar mucha lógica en el cliente. Si hablamos de una página web al uso, donde a penas se usa JavaScript para un par de animaciones, sería matar moscas a cañonazos. ¿Tiene sentido en Ducksboard? Sin duda, sí. El frontend de Ducksboard trata de parecer una aplicación nativa y ser muy interactivo (una interfaz rica de esas). Eso implica muchísimo JavaScript, y evitar accesos al servidor en la medida de lo posible para no ralentizar demasiado el asunto. El caso de uso perfecto para estos frameworks MVC.
Bien, justo lo que necesitábamos para solventar nuestro problema. Construir sobre herramientas existentes y probadas es siempre un colchón de seguridad, así que ahora sólo tocaba escoger uno de esos frameworks. Todos hacen cosas parecidas, claro, pero tienen pequeñas diferencias a nivel de filosofía e implementación. Cuando me estuve mirando las opciones existentes hace cerca de un año, había tres que acaparaban gran parte de la atención:
- Backbone.js: es pequeñito y manejable, de la gente del imprescindible underscore.js. Se centra en la “M” del MVC, ofreciendo mecanismos de actualización y sincronización de los modelos, por defecto usando REST + JSON. También ofrece soporte para eventos y rutas (URLs que ejecutan funciones JavaScript en vez de navegar a otro documento).
- Knockout.js: al contrario que Backbone, se centra en la “V” del MVC. La gracia de Knockout es el vincular elementos del DOM (vistas) a sus modelos, de modo que cada vez que se cambia un atributo en un modelo se actualiza su vista (o elemento del DOM), o viceversa. La sincronización de modelos con el servidor la dejan como ejercicio para el usuario.
- batman.js: un MVC en CoffeeScript. Además del uso de dicho lenguaje, la gracia radica en que se integra con Node.js. La idea es que puedan usarse los mismos modelos en el servidor (aplicación Node) y en la aplicación de frontend, eliminando redundancia y posibles incongruencias.
- Ahora existen un par más, pero por aquel entonces o no hacían mucho ruido o no supe verlos. Echad un ojo a Ember.js y a CanJS.
El retorno del Rey
Si le echáis un ojo a la documentación de Backbone, concretamente esta sección, veréis que no imponen, ni siquiera recomiendan, una manera concreta de usar el framework. Desde ese punto de vista es más una librería que un framework, de hecho. No te dicen como vincular modelos con vistas, ni si la relación entre ambos es 1:1 o n:m, ni si has de crear controladores como intermediarios, … Nada. Básicamente que lo hagas como te parezca, que va a funcionar igual, ellos simplemente ofrecen una serie de clases base con cierta funcionalidad. Eso fue un pequeño freno al principio, porque hubo que decidir como íbamos a ordenar todo aquello, y las decisiones de diseño han de tomarse con calma y teniendo muchas cosas en cuenta, que sino luego la lías parda.
Lo que sigue es una revisión de como ha quedado la cosa, o dicho de otra manera, como diantres usamos Backbone en Ducksboard. Puede ser de ayuda como inspiración para otras aplicaciones, o no serlo en absoluto: se han tomado las decisiones de diseño en base a nuestras necesidades y funcionalidades y no las tuyas, se siente :)
Tenemos modelos para widgets y dashboards, por supuesto. También para cosas menos “tangibles” como el usuario autenticado o sus servicios registrados (Twitter, Analytics, …). Los widgets se componen de slots, que son quienes realmente chupan y dibujan datos, que por descontado tienen sus modelos (gráficas, timelines o imágenes son slots, y los widgets se crean uniendo varios de ellos). De este modo, un dashboard tiene una colección (es como llaman a una lista de modelos en Backbone) de widgets, y cada uno de estos tiene a su vez una colección de slots.
Cada modelo puede tener varias vistas. Nuestra relación de modelos a vistas es 1:n. Por poner algunos ejemplos:
- Un widget tiene su vista principal, que es la de visualización de datos, pero también una de configuración, que es un modal con tabs y formularios.
- Un dashboard tiene su vista principal, con su background y sus widgets ocupando toda la pantalla, pero también una vista de lista, donde aparece simplemente como un link en una lista de dashboards que permite cambiar a cualquiera de ellos, y además una lista de configuración, con un modal mostrando formularios.
Backbone no impone absolutamente nada en cuanto a vistas. Una vista es una instancia de una clase que tiene un método render() que se encarga de dibujar lo que haya que dibujar. En ese método se puede hacer lo que uno quiera. Usar librerías de templating, generar DOM a mano con jQuery, lo que sea. En nuestro caso las vistas renderizan templates de jQuery Templates. ¿Por qué jQuery Templates, que está descontinuado, y no algo más usado como mustache? Pues porque jQuery Templates tiene una cosa útil para nosotros: se puede pasar un objeto a la función de renderizado del template, obtener un elemento de DOM, y poder recuperar ese objeto a partir del elemento de DOM en cualquier momento. Eso nos permite saber con que widget (modelo de Backbone) estamos interactuando al clickar en el elemento de DOM correspondiente.
Cuando un widget o dashboard es modificado a través del modal de configuración, los atributos del modelo correspondiente se actualizan y se persiste el modelo en el backend. Backbone hace esa parte fácil: genera un JSON representando los atributos del modelo y lo envía al backend con un POST o un PUT en función de si el modelo es nuevo o ya existente. Nuestro backend está encantado, ya que nuestra API habla REST y JSON. Todo muy bien.
Usamos los eventos de Backbone sobretodo en actualizaciones de datos de widgets. Cuando un slot (recordemos que los widgets están compuestos de slots) se inicia, se registra a un tipo de evento concreto. Cuando el handler de WebSocket recibe una actualización inspecciona el mensaje, y en base a su contenido provoca un evento que sólo los slots interesados escucharán. De ese modo varios slots / widgets pueden actualizarse a la vez si están interesados en una misma fuente de datos.
Por último, la mayoría de acciones en Ducksboard se activan accediendo a URLs locales gracias a las rutas de Backbone. De ese modo, un cambio de dashboard se inicia siguiendo el enlace #dashboard/nombre-de-dashboard, o la pantalla de configuración de un widget se invoca accediendo a #settings/widget/id-de-widget. Eso permite bookmarkear cualquiera de esas ubicaciones, lo que facilita el soporte a usuarios o la compartición de enlaces (¡recordemos que tenemos una única página!).
evasión o VICTORIA
Y bueno, eso es todo por esta vez, amigos. El cuándo, el porqué y el cómo de Backbone.js en Ducksboard. Teníamos un problema, decidimos corregirlo en vez de mirar a otro lado, y la cosa salió bien. Nos desvió del planning inicial, cierto. Y fue un tanto “coñazo” reescribir todo el frontend, cierto otra vez. Pero mirando hacia atrás, era la única decisión correcta. De no haberlo hecho, hoy sería imposible tener el frontend que tenemos y seguir extendiéndolo como lo hacemos.
Os invito a compartir en los comentarios vuestras experiencias con MVCs JavaScript, y a reprocharme el estirar tanto el artículo si os he robado demasiado tiempo. Gracias por leer :)
