Optimiza un monorepo con Nx usando tags y constraints para asegurar una arquitectura limpia, sin dependencias circulares y con control desde el linter. Aquí verás cómo etiquetar proyectos en project.json, definir reglas en nx.json y replicarlas en la configuración de ESLint para que todo se verifique automáticamente.
¿Cómo usar tags en Nx para clasificar proyectos?
Asignar tags por scope a cada proyecto permite identificar su función y controlar dependencias. Se edita cada project.json dentro de packages y se agrega el arreglo de etiquetas.
App1: scope app.
App2: scope app.
Server: scope API.
UI shared: scope UI.
Utils common: scope utils.
Ejemplo mínimo en un project.json de una app:
{"name":"app1","tags":["scope app"]}
Ejemplo para UI shared:
{"name":"ui-shared","tags":["scope UI"]}
Y para utils common:
{"name":"utils-common","tags":["scope utils"]}
¿Qué reglas de dependencia definen las constraints en nx.json?
En nx.json se agregan las constraints para limitar qué puede depender de qué. Así se evitan dependencias circulares y cada capa mantiene su responsabilidad. El control lo aplica el linter cuando analiza los proyectos.
Con esto, por ejemplo, utils no puede depender de UI, y UI no puede depender de API. Al romper una regla, el linter lo marcará.
¿Cómo replicar las constraints en ESLint y validar con el linter?
Las mismas restricciones se copian en la configuración de ESLint para que el análisis estático refleje las reglas de nx.json. Al ejecutar el linter, se verifican las capas y no pasa nada que viole las dependencias establecidas. En la validación mencionada, los tres proyectos analizados pasaron correctamente.
Fragmento conceptual de configuración para ESLint:
Para añadir o modificar las etiquetas ('tags') de un proyecto específico, debes hacerlo en el archivo project.json correspondiente a dicho proyecto. Allí, puedes asignar el scope deseado, como por ejemplo scope:ui para una nueva biblioteca de componentes. Asegúrate de guardar los cambios después de realizar esta modificación. Esto permitirá clasificar y controlar las dependencias del proyecto adecuadamente utilizando las herramientas de NX.
🦄✨Dependencias circulares
Una dependencia circular ocurre cuando el proyecto A depende del B, y al mismo tiempo, el B depende del A. Esto es un problema porque crea un bucle infinito que puede romper el proceso de compilación y hace que el código sea muy difícil de mantener.
Una configuración correcta previene esto al crear una arquitectura en capas:
Capa más baja (utils): No puede depender de nadie más que de sí misma. Es la base.
Capa media (ui, api): Pueden usar utils, pero no pueden depender de app. Es importante notar que ui no puede depender de api y viceversa.
Capa más alta (app): Puede usar todo lo que está debajo (utils, ui, api), pero nada puede depender de app.
Ejemplo de violación: Si se intentara importar una función de ui-shared (scope:ui) dentro de un archivo en utils-common (scope:utils), ESLint mostraría un error porque las reglas dicen que scope:utils no puede depender de scope:ui. Esto rompe el flujo unidireccional de las dependencias y es exactamente lo que estas reglas están diseñadas para evitar.