Evasión o victoria, o de como Backbone.js salvó el frontend de Ducksboard

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.

Nos acabamos decantando por Backbone.js. No usamos CoffeeScript ni Node.js, con lo que batman.js no aplicaba. Las vistas vinculadas a modelos de Knockout no nos eran demasiado útiles, ya que los elementos que se actualizan en nuestro caso son los widgets, y no lo hacen en base a atributos del modelo, sino por los datos que llegan por WebSocket. Y nuestra API (en construcción en aquel momento) era REST + JSON, así que era consumible por Backbone out of the box. Además, su soporte para rutas era algo que nos venía de perlas para hacer ciertas acciones bookmarkeables. En definitiva, cubría todas nuestras necesidades y además con un código pequeño y comprensible, lo que nos daba más seguridad. Y así me sumí en la reescritura del frontend de Ducksboard usando Backbone.js. ¡A por ellos, que son pocos y cobardes!

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 :)

Advertisements

16 thoughts on “Evasión o victoria, o de como Backbone.js salvó el frontend de Ducksboard

  1. Muy buen artículo compañero! Se agradece encontrar información comparativa de pros y contras, frente a apuestas ciegas por un entorno determinado.

    Totalmente de acuerdo con vuestra visión y conclusión: Cuando uno está en plena fase de crecimiento, a veces no queda otra que dar marcha atrás y retroceder unos cuántos pasos, para poder crecer más rápido y llegar más lejos.

    Un saludo

  2. Genial atículo, y refrescante. Me gusta leer artículos de profesionales que no les duele en absoluto decir “oye, hicimos esto y la agamos de verde, porque? por esto” tan dificil será?

    Ademas, usas un lenguaje distendido y, aunque pienses que es un truñaco, se lee muy rápidamente justo por ese motivo, muy ameno.

    Y por supuesto, el contenido fantástico, comentando varias opciones “availables” y diciendo el porqué y porqué no habeis elegido cada una.

    Bravo! sigue (seguid) asi!!! :-)

  3. Tuvimos una experiencia muy parecida, este artículo lo hubiese podido escribir yo mismo, sin la gracia y el salero, claro ;) Teníamos una sopa de jquery intratable y backbone lo puso todo en su sitio con muy poco esfuerzo.

    Sufrí un poco durante el tiempo que pasó entre tomamos la decisión de usar backbone y que vimos que empezaba a funcionar (y que cumplíamos los plazos de entrega ;)

    Gracias por el artículo.

    1. Bueno, en nuestro caso fue bastante esfuerzo, la verdad. Tenemos un porrón de JavaScript y hubo que reescribirlo absolutamente todo, pero el resultado bien vale la pena.

      No volveré a infravalorar el desarrollo de un frontend en la vida xD

      1. Si, era un proyecto que habíamos empezado hacía poco, bastante sencillo pero que tenia una parte muy “rich internet application” :P, fué aqui dóndo nos salvó pero para el resto también simplificó mucho las cosas.

  4. Hola Aitor,

    Gracias por el artículo, echo de menos artículos de este tipo de otros proyectos españoles, en cuanto saque un hueco escribiré uno sobre mivecindad.com.

    En mivecindad usamos ExtJS como framework. Desde la versión 4 es un framework MVC completo y realmente potente en los tres niveles. Ofrece de serie un montón de widgets para la vista y bastante funcionalidad ya implementada (drag&drop, menus, gestión de eventos, etc). Eso sí, si no vas a usar la V que ofrece se queda un poco cojo, aunque es lo suficientemente extensible como para desarrollar tus propios widgets. En mivecindad se puede decir que tenemos dos aplicaciones completas, una del lado del servidor y otra del lado del cliente, sólo que la fuente de datos de una es una BD y de la otra es la API REST del servidor.

    Otro aspecto atractivo es la posibilidad de adaptar tu app para móviles reutilizando por completo todo el modelo usando Sencha Touch.

    Un saludo!

  5. Interesante hasta para mí, que no tengo npi de programación, pero las comparaciones con pelis son divertidas…un abrazo crack!

  6. Mas posts de este estilo hacen falta en el panorama tecnico patrio. Un placer leer este tipo de textos. Justamente ando pensando sobre frameworks javascript. He jugado con ember.js y con backbon y realmente este ultimo parece mucho mas senzillo.

    Gracias por el post!

  7. Pingback: Noticias 31-05-2012 - La Web de Programación

  8. Genial literatura!, es como ver mi futuro a corto plazo!
    Vuestra APP es una pasada, si no fuese por el maldito IE…
    Puedo preguntar que tiempo ha llevado este desarrollo?, estimado XD

    Mucho animo y enhorabuena por el trabajo

    1. Bueno, soportamos, más o menos, IE9. No tenemos demasiado interés en soportar versiones anteriores, ni en perder mucho tiempo en hacerlo funcionar al 100% en 9 o 10 cuando hay otros navegadores mucho mejores disponibles.

      La app entera hasta la primera release beta nos llevó unos 6 meses. Pero claro, desde entonces no hemos dejado de añadir / mejorar cosas, el desarrollo de una aplicación siempre es infinito :)

  9. Pingback: El que muere paga todas sus deudas « Take it easy

  10. Pingback: De safari con Chrome: cazando leaks en Javascript | Take it easy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s