A fondo con NodeJS 0.12
Un artículo de Rafa G. Blanes
Me atrajo NodeJS desde un principio, cuando oí eso de código de servidor escrito en javascript. Después de aprender las nociones básicas hace un par de años, integrarlo con multitud de módulos y estar a punto de terminar un proyecto completo con este framework, en el que he estado trabajando en el último año, puedo decir que aún viendo sus pros pero también sus contras, sigue siendo para mí una maravillosa plataforma sobre la que construir aplicaciones escalables y mantenibles.
Framework, plataforma, tecnología... se mezclan un poco los conceptos: NodeJS te ofrece el core o framework esencial sobre el que construir aplicaciones, junto con un modelo de programación basado en eventos y una serie de módulos básicos pero muy importantes.
Lo que le da vida realmente a un proyecto con NodeJS es el enorme y vasto ecosistema de módulos que existe a su alrededor, que fueron apareciendo desde el primer momento en que NodeJS salió a escena y que hoy día siguen creciendo y evolucionando.
Esto es algo típico y la tendencia natural en software, como Drupal, Wordpress o el recién salido del horno ASP.NET 5 como framework de aplicaciiones web, sin cuya constelación de módulos que extienden o mejoran su funcionalidad no serían lo que son hoy en día.
Pero no es sólo cuestión de elegir e integrar un conjunto de módulos: es necesario utilizar un grupo de herramientas para una correcta gestión de la configuración de tu proyecto y para la puesta en marcha de los flujos de trabajo que hayas definido, además de aplicar continuamente buenos principios de desarrollo, clean code y una refactorización siempre presentes.
En este proyecto web con un backend bastante pesado en el que llevo trabajando un año he tenido oportunidad de usar NodeJS en profundidad, leyendo multitud de libros, muchos posts y artículos sobre buenas prácticas y buscando y analizando los módulos más populares o maduros para incorporar toda la funcionalidad.
A continuación cuento un poco mi experiencia que básicamente se basa en la pregunta recurrente de cómo hago esto y ahora cómo hago esto otro aún mejor y así hasta que el proyecto comienza a alcanzar cierta madurez, cuando todo el trabajo realizado comienza a converger en algo más palpable y empieza a verse un proyecto que ofrece cierta utilidad, o eso es al menos lo que esperas.
He utilizado una aproximación lean al proyecto, esto es, voy a cerrar un conjunto de funcionalidad muy bien definida y después es el mismo proyecto el que va a validar con los usuarios (los primeros son siempre tus amigos y familiares) si es de alguna utilidad..., si hay que seguir (;-), pivotar (;-| o comenzar otra cosa distinta (;-(.
Me fascina la aproximación lean, ya que en realidad construyes experimentos que después el mercado valida. Si no sabes qué es la aproximación lean de proyectos, comienza leyendo el libro clásido de Eric Ries.
En cuanto a los módulos que estoy usando, los agruparé funcionalmente.
En el core de la aplicación, puedo desglosar los siguientes módulos principales:
- Express como framework para lo esencial de una aplicación web. Prácticamente es lo estándar en el mundo NodeJS y posee una enorme constelación de middlewares para la gestión de sesiones (express-session), gestión de los parámetros de un request o post (body-parser), cookies (cookie-parser), enrutado (lo gestiona directamente express), seguridad (protección CSRF con csurf), etc. Aunque hay otras opciones más flexibles, he usado ejs como herramienta de plantillas. También he usado compression para el envío comprimido de los contenidos en cada request.
- Compresión de ficheros con archiver.
- Gestión de imágenes con fabric.js (sí, tiene un módulo que permite usar fabric en el lado del servidor) así como canvas.
- Obtención de valores únicos y hashs con node-uuid, hashids (para la generación elegante de enlaces cortos) y md5 (para crear valores hash a partir de cadenas de texto).
- Framework para logueo de usuarios con Passport; sus extensiones te permiten integrar fácilmente loguee con Facebook, Google+ o Twitter.
- Q como librería para usar promises y evitar el anidamiento de funciones asíncronas (callback hell) que impiden escribir un código legible.
- Cliente para usar Redis como servidor de objetos de caché y almacenamiento en ella de sesiones de usuario.
- Search-index como mecanismo sencillo y útil para indexar los contenidos del site y poder realizar búsquedas.
- Módulo cliente de Sendgrid para el envío de correos electrónicos usando ese mailer.
- cron para gestionar la ejecución de operaciones recurrentes de gestión y mantenimiento del site.
- winston como librería avanzada para mensajes de log.
- Para escribir código más legible y mantenible, he usado profusamente lodash (esto es una oblicación para cualquier desarrollador que use javascript, aunque hay otras opciones similares).
- html-minifier para la minificación de contenidos al ser enviados en cada request.
No es que haya elegido todos esos módulos al azar, sino después de un proceso y trabajo de prueba, evaluación y experimentación, así como leyendo muchos blogs y viendo lo que la comunidad usa con más frecuencia.
¿Y qué hay de las pruebas? Ningún proyecto software puede considerarse maduro y profesional si no está respaldado por pruebas.
Para ello he usado, cómo no, chai como librería de asersiones y mocha para la ejecución de las pruebas. El resultado y la experiencia con ambas librerías ha sido y es magnífico.
El site gestiona multitud de ficheros de diverso tipo (no, no es una página más de enlaces y descargas...), de modo que después de evaluar diversas opciones cloud como CDNs me decanté com Amazon AWS y su paquete aws-sdk para la integración de su API REST en NodeJS.
Del mismo modo, el site se apoya en una base de datos MySql, para lo que he usado el cliente mysql para su integración con la aplicación.
El desarrollo de una aplicación no consiste sólo en hacer que esta funcione, más o menos respaldada por pruebas: también hay que definir flujos de trabajo para generar los assets del proyecto que finalmente serán desplegados en producción.
Para ello he usado, cómo no, grunt y muchos módulos básicos para generar la aplicación para su despliegue. Aunque su mismo nombre indica su propósito, a continuación indico los más relevantes:
- grunt-contrib-concat, para la concatenación de ficheros.
- grunt-contrib-uglify, para ofuscar código javascript.
- grunt-angular-gettext; este módulo está relacionado con la solución de multilenguaje que he usado basada en AngularJS y ficheros po, genial.
- grunt-contrib-cssmin, para minificar ficheros CSS.
- grunt-contrib-copy; copia ficheros de un origen a un destino; lo he usado para copiar dentro de la carpeta /build lo imprescindible para desplegar la solución.
- grunt-contrib-clean; hace trabajos de limpiez después de una compilación para generar la solución desplegable.
Puff, más de uno que se acerca a NodeJS por primera vez pensará que es mucha la cantidad de módulos externos, librerías y frameworks los que hay que usar para generar una aplicación completa y profesional; pues así es, y ahí radica precisamente lo positivo, ya que al ser todo tan modularizable puedes elegir única y exclusivamente aquello que mejor te viene o más te gusta y conoces. Al final tienes una aplicación en producción sin overhead de cosas que no usa y muy ligera porque no le sobra ni le falta nada.
Ahora bien, más importante que todo lo anterior, que usar correctamente y saber elegir los módulos más apropiados, es trabajar duro para que la distribución y organización de todos los módulos y assets del proyecto que has implementado sea lo más simple y legible posible. Proyectos muy buenos con muy mala organización terminan convirtiéndose en un dolor de cabeza al no tener organizados correctamente sus assets.
Aquí cada uno al final llega a su propia guía personal, pero basada en cómo los demás hacen las cosas. Yo he seguido las siguientes reglas:
- La mayoría de los módulos que he implementado en la solución son muy simples, cortos y ofrecen una funcionalidad muy concreta y coherente. Muchos han surgido como resultado de una refactorización continua. Cuanto más simples y concretos, más fácilmente se podrán testear.
- Los assets del frontend (basado en AngularJS) están completamente separados del backend; sí, es una obviedad, pero lo tengo que indicar...
- He definido una forma coherente de nombrar los ficheros de tests en relación a los módulos que prueban; así se localizan fácilmente.
- He usado mi propio mecanismo de inyección de dependencias.
- Están claramente separados todos los módulos relacionados con el mantenimiento del sistema.
No es que haya que hacer esto así a la fuerza por ser un proyecto basado en NodeJS, sino que esto no es más que la sencilla aplicacíon de principios que deben estar presentes en cualquier proyecto software, como indicaba hace un tiempo en este post y sobre lo que hablo profunsamente en El Libro Negro del Programador.
Es más, te das cuenta, sobre todo al principio, de que cuando te aproximas a una nueva tecnología o entorno por primera vez, más necesitas refactorizar continuamente, ya que al aprender más sobre ella y profundizar te das cuenta cómo mejorar todo lo realizado hasta el momento.
Estoy muy contento con este nuevo proyecto en el que he ido avanzando a golpe de microtareas en noches, fines de semana y días de vacaciones. Como digo muchas veces, sólo se puede aprender algo haciendo y no teorizando al leer sobre ello continuamente (learning by doing).