Todos sabemos cómo la E/S asíncrona de Node lo ha convertido en una de las herramientas de-facto para desarrollar servicios (normalmente APIs REST, aunque otros tipos, no tan populares, también son bastante comunes). Y si has trabajado en algún proyecto de tamaño medio (o mayor), probablemente hayas experimentado el proceso de dividir la funcionalidad entre servicios individuales.
Esta práctica es genial para ayudar a mantener el código desenredado, es fantástico para permitir despliegues parciales y disminuye los riesgos de un fallo catastrófico masivo al actualizar código antiguo (porque la cantidad de daño que puedes causar se limita a un servicio en particular).
Dicho esto, si has experimentado lo anterior, probablemente te has encontrado con el problema de tener librerías internas personalizadas que necesitan ser compartidas entre los servicios de tu proyecto.
Por ejemplo: la envoltura de registro que escribiste alrededor de Winston, o esa librería de comunicación que escribiste para enviar datos entre servicios.
Recuerda que si tu quieres aprender más acerca de Node puedes hacerlo en el siguiente video, o puedes adquirir el curso completo aquí
Las formas actuales de resolver este problema
Estoy seguro de que a cualquiera se le pueden ocurrir formas alternativas de solucionar este problema, pero dos métodos muy comunes de hacerlo son:
Compartir tu código en NPM
Claro, usted puede tomar el tiempo para crear un proyecto separado para su código común y compartirlo en NPM. A primera vista, esto podría parecer una gran idea y la solución perfecta, después de todo, realmente encaja con la descripción del trabajo de NPM, ¿no?
Sin embargo, si lo miramos desde la perspectiva de que se trata de un pequeño trozo de código de una base de código más grande, y de repente necesitas compartirlo con otros, hay un par de razones por las que esto puede ser un poco problemático. Permítanme explicar:
- Puede que no utilices un registro privado. Después de todo, estás compartiendo tu código personalizado con otros compañeros de trabajo que desarrollan los otros servicios para tu proyecto, realmente no quieres que otras personas de todo el mundo utilicen tu código (¡menos aún que lo vean, ya que puede contener bits personalizados y propietarios que deben permanecer en secreto!) Y como probablemente sepas, NPM te permite usar repositorios privados, donde puedes publicar tu código personalizado sin publicarlo al mundo. Configurar uno requiere tiempo y recursos, así que aunque esta podría ser una solución para este caso, podría no ser rentable si sólo tienes un par de librerías personalizadas que estás tratando de compartir entre módulos.
- Incluso si el punto anterior no fuera un problema para ti, tener que extraer el código para tu código común en un proyecto separado y luego crear y publicar adecuadamente el módulo requiere esfuerzo. Ojo, no es excesivamente complicado compartir tus módulos, pero se requiere algo de refactorización, por pequeña o grande que sea y puede que no tengas tiempo o mano de obra para hacerlo.
- El código que has extraído ya no está disponible para ti. Sí, está dentro de la carpeta
node_modules
, que es un lugar donde muy pocos desarrolladores se atreven a entrar (o incluso saben dónde buscar). La cuestión aquí es que literalmente has eliminado el código de tu base de código y lo has convertido en una entidad genérica y externa. Esto esencialmente hace que sea más difícil de mantener, ya que ahora es un nuevo proyecto, con su propia base de código. Y eso es sólo si estamos hablando de una biblioteca, ¿qué pasa si estás extrayendo tres? ¿O cuatro? ¿Quién la mantiene y cuándo lanza las actualizaciones?
Aunque a primera vista esta solución pueda parecer el camino a seguir, a la larga puede resultar engorrosa e incómoda de mantener.
Cómo solucionarlo con GIT
Hay dos maneras en las que GIT puede potencialmente ayudarte aquí (y quizás otros sistemas de control de versiones puedan hacer lo mismo, pero no estoy muy familiarizado con ellos, así que me ceñiré a lo que conozco):
- Mover el código genérico a un repositorio diferente, esencialmente haciendo algo similar a lo que hiciste con NPM, pero ahora necesitas tener submódulos en tu carpeta. Eso no puede salir mal, ¿verdad?Quiero decir, que levante la mano el que nunca haya tenido problemas en los que uno de sus compañeros de equipo (o incluso usted) haya fastidiado el repositorio del proyecto utilizando un repositorio GIT normal, y mucho menos añadiendo submódulos a la mezcla. Son una solución elegante que GIT proporciona para resolver el problema del que estoy hablando, pero seamos honestos, la herramienta en sí sigue siendo demasiado ajetreo para muchos desarrolladores como para siquiera pensar en añadir la complejidad extra de lidiar con este enfoque.
- En lugar de añadir un nuevo repositorio, puedes reunirlo todo en un monorepo. Después de todo, ¡todo el mundo lo hace hoy en día! Así que en lugar de tener tus servicios divididos en repositorios individuales, y tus módulos genéricos en aún más repositorios, crearás uno solo, y añadirás todo en él. Dependiendo de tu configuración, puede ser una buena idea para ti, pero piénsalo: ¿cuántos mini-proyectos individuales necesitas mantener? Y añadirles los módulos genéricos, ¿cuántos son? Para tener todo dentro de un único repositorio la orquestación tiene que estar increíblemente bien ejecutada. Y créeme, lo he hecho en el pasado, se puede hacer, pero sólo lo recomendaría como último recurso.
Así que, por el momento, dejemos a GIT fuera de juego, ¿de acuerdo? O al menos, dejémoslo fuera de la solución y no lo toquemos si ya te está funcionando.
El concepto de componentes es esencialmente cualquier cosa que quieras compartir, ya sea un único archivo con una definición de clase, un conjunto de funciones o una carpeta entera llena de bibliotecas genéricas.
Sea lo que sea en lo que estés trabajando y de repente te has dado cuenta de que se puede compartir, puedes exportarlo a otros. Entonces, ¿cuál es la diferencia con NPM?
- Para empezar, no estás eliminando el código de tu base de código. Considero esto una gran ventaja porque, de repente, estás tratando con contenido compartido sin tener que separarlo del resto de tu proyecto. Sigues siendo el mantenedor del mismo, porque lo creaste después de todo, pero al mismo tiempo, eres capaz de compartirlo como un módulo npm (te mostraré cómo en un segundo).
- Tu código compartido (no importa cuántos componentes individuales diferentes estés compartiendo) permanece dentro de tu repositorio de código. No necesitas añadir orquestación extra a tu solución de versionado de código para tener una parte de él compartida. Si necesitas seguir actualizándolo, simplemente actualiza el código y haz que la herramienta CLI de Bit extraiga la última versión en el proyecto de destino.
A diferencia de NPM, Bit comprobará el árbol de dependencias de tu componente. Lo que esto significa es que si sólo eliges compartir un archivo, pero requiere otros archivos locales como dependencias, bit te lo dirá y te permitirá añadirlos como parte del componente.
Esencialmente, bit te permite resolver el problema con un enfoque similar al #1, pero te proporciona los recursos que necesitas para hacerlo realmente bien:
- El registro privado es proporcionado por Bit, así que no necesitas preocuparte por esa parte.
- No necesitas configurar un nuevo módulo npm para ser publicado, con sólo unos pocos pasos, puedes compartir el código sin necesidad de realizar ningún tipo de refactorización.
- De nuevo, no estás extrayendo el código del proyecto principal donde se originó. Permanece en el mismo lugar, y dentro del mismo repositorio. El impacto en el proyecto es casi nulo.
Utilizar Bit para compartir tus componentes
¿Cómo compartimos nuestro código entonces? Tienen una página de documentación muy detallada donde puedes comprobarlo con más detalle, pero para simplificar, te mostraré cómo compartir un componente simple de un proyecto a otro con pasos rápidos.
El componente que voy a compartir es un objeto logger, que es simplemente una instancia de Winston:
const winston = require("winston")
const config = require("../config.json")
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: config.logging.output_files.error, level: 'error' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
module.exports = logger
Paso 1: Instalar bit
Puedes instalar bit de diferentes maneras, pero la más fácil y genérica para el sistema operativo sería usar npm de esta forma:
$ npm install bit-bin --global
Paso 2: Iniciar sesión
Una vez instalado, necesitas iniciar sesión o registrarte, esto es extremadamente simple de hacer, especialmente si ya tienes una cuenta de Github. Desde la línea de comandos, sólo tienes que escribir:
$ bit login
Ese comando iniciará tu navegador y abrirá la página de inicio de sesión para Bit. Una vez allí y una vez que hayas completado las acciones de registro / login, podrás empezar a configurar qué compartir.
Ten en cuenta también que al iniciar sesión, estás añadiendo un nuevo ámbito al archivo de configuración de NPM. Ahora tendrás acceso al ámbito @bit
, que te permitirá instalar componentes como si fueran los módulos clásicos de NPM (más sobre esto en un segundo).
Paso 3: Inicializando el espacio de trabajo
Bit utiliza el concepto de espacio de trabajo para agrupar colecciones (que son grupos de componentes). Lo primero que necesitas hacer, es inicializar tu espacio de trabajo, y puedes hacerlo simplemente escribiendo:
$ bit init
Eso es todo, una vez hecho esto, podrás empezar a decidir qué compartir.
Paso 4: Configurar un compilador
Bit ofrece varios compiladores preconfigurados para los componentes gestionados por el espacio de trabajo. En nuestro caso, utilizaremos el componente compilador Babel. Esto nos permitirá crear un código distribuible para nuestro componente 'logger', sin depender de ninguna configuración específica en el entorno de autor.
Nuestro componente será fácilmente mantenible por otros, en otros contextos, gracias al componente compilador Babel estandarizado que utiliza.
$ bit import bit.envs/compilers/babel --compiler
Paso 5: Añadir archivos y comprobar el estado de los componentes
Añadir los archivos que desea compartir es bastante sencillo. Asumiendo una estructura de proyecto como la siguiente:
Para añadir archivos, basta con hacer:
$ bit add lib/logger.js
Esto añadirá el archivo y creará un nuevo componente llamado "logger". Por defecto, el comando add nombrará el componente usando el nombre del archivo, puedes revisar la documentación completa de este comando para ver todo lo que puedes hacer con él.
Ahora puedes realizar una comprobación de estado para saber si tienes todo lo que necesitas:
$ bit status
La captura de pantalla anterior muestra la salida de las comprobaciones realizadas por bit. Aquí es donde la CLI crea y comprueba el árbol de dependencias para nuestro módulo. Si nos fijamos en el código de antes, te darás cuenta de que estoy requiriendo un archivo JSON que no he añadido todavía. Este es uno de los beneficios de usar bit en lugar de ir por la ruta npm, podemos evitar perdernos archivos importantes.
Después de añadir el otro archivo, puedes realizar una nueva comprobación de estado y obtendrás una respuesta con mejor aspecto.
Paso 6: Versionado
Antes de subir los archivos, tendrá que etiquetar la versión del componente. Esto etiquetará todos los componentes, por lo que es una buena manera de inicializarlos todos al mismo tiempo:
$ bit tag --all 0.0.1 --message "initial version for the component"
Este paso es obligatorio, no podrás confirmar nada hasta que etiquetes la primera versión.
Paso 7: Exportar el componente
Una vez que todo lo anterior está listo, exportar los componentes requiere que crees la colección donde van a vivir. Eso se hace desde su página web y una vez creada, basta con hacer:
$ bit export <account-name>.<collection-name>
En mi caso, he llamado a la colección "custom-logger"
y mi nombre de cuenta es "deleteman"`
, por lo que mi comando quedaría así:
$ bit export deleteman.custom-logger
Eso subirá el archivo al registro personalizado, sin hacer nada a tu código ni a tu repo.
Paso 7: Usarlo en otro sitio (opcional)
Si necesitas usar tu componente en otro proyecto, y supongo que lo necesitarás, ¿qué demonios estás haciendo aquí? Podrás instalarlo usando NPM simplemente escribiendo:
$ npm install @bit/<account-name>.<collection-name>.<component-name>
Así que, en nuestro caso:
$ npm install @bit/deleteman.custom-logger.logger
Aunque estoy haciendo referencia a uno de los componentes, todas las dependencias también se instalarán, por lo que para usarlo, simplemente requiérelo así:
const logger = require("@bit/deleteman.custom-logger.logger")
logger.info("Testing test!")
Por supuesto, la caja donde estás ejecutando el comando npm install tendrá que estar primero logueada en bit, como te mostré en el paso #2 (para tener el ámbito de bit configurado para npm y también, para tener acceso a los componentes ya que también pueden ser privados).
Conclusión
Con estos sencillos pasos, has conseguido compartir código de tu proyecto, sin tener que sacarlo de él, y has conseguido usarlo en otro, con el mínimo esfuerzo.
Como este artículo ya es bastante largo, he decidido dejar fuera pasos opcionales, como mostrarte cómo va la interacción entre GIT y Bit, o cómo actualizar el contenido de un componente y su versión y cómo eso se puede trasladar a todos los proyectos relacionados. Hay mucho más detrás de estos pasos básicos, pero esperamos que esto haya sido suficiente para mostrarte los beneficios de usar un servicio de este tipo para compartir código común entre proyectos relacionados con el mínimo esfuerzo.