Dividir una aplicación Angular en aplicaciones micro-frontend: guía paso a paso
Introducción a los micro-frontends
Los micro-frontends ofrecen una solución escalable para gestionar aplicaciones grandes dividiéndolas en piezas más pequeñas e independientes. Este enfoque permite que equipos especializados desarrollen, prueben y desplieguen secciones de una aplicación por separado, lo que mejora la eficiencia y reduce la complejidad.
¿Qué es un micro-frontend?
Los micro-frontends aplican los principios de los microservicios al desarrollo front-end y dividen las aplicaciones grandes en partes manejables. Estas partes, o micro-apps, se pueden desarrollar de forma independiente e integrarse, a menudo mediante el enrutamiento. Para profundizar en los micro-frontends, especialmente en lo relativo a Module Federation, te recomendamos nuestro artículo sobre Module Federation.
Perspectivas
Los usuarios ven una aplicación unificada, mientras que los desarrolladores disfrutan de flexibilidad en las opciones de despliegue y de servicio, incluido Module Federation para integrar las micro-apps.
Panorama tecnológico de los micro-frontends
Los micro-frontends permiten usar distintos frameworks o bibliotecas en diferentes partes de la aplicación, lo que ofrece flexibilidad pero también plantea retos, como la movilidad entre equipos. Herramientas como Single SPA o soluciones específicas de cada framework, como Nx para Angular, ayudan a integrar estas tecnologías en una aplicación cohesionada. El objetivo es compilar, probar y desplegar cada micro-app de forma independiente, garantizando al mismo tiempo una experiencia de usuario fluida.
Abordar la transición a micro-frontends
El reto de las aplicaciones grandes
Las aplicaciones grandes pueden sufrir tiempos de prueba y de build prolongados, además de un servicio local lento. Hacer la transición a micro-frontends dividiendo la aplicación en partes más pequeñas puede aliviar considerablemente estos problemas.
Hay numerosos métodos para implementar micro-frontends, y existen diversas guías y recursos disponibles. Algunos se centran en construir micro-frontends desde cero, lo que resulta ideal para proyectos nuevos que se espera que crezcan considerablemente.
Refactorizar una aplicación existente
Sin embargo, esta guía se centra en refactorizar una aplicación existente, posiblemente creada con Angular CLI, en bibliotecas compartidas y aplicaciones individuales. Después, estos componentes se integran sin fricciones en la aplicación principal, manteniendo una experiencia de usuario unificada y mejorando la eficiencia del desarrollo.
Revisar la aplicación base para implementar micro-frontends
Configuración del experimento con una aplicación de ejemplo
Para explorar los micro-frontends, usaremos una aplicación de ejemplo. Aunque no es grande ni compleja, incorpora varios servicios que muestran cómo se pueden integrar con cada micro-app en una arquitectura de micro-frontends.
Estructura de la aplicación
La estructura de la aplicación refleja lo que normalmente se obtiene al crear un proyecto nuevo con Angular CLI. Sus elementos clave son:
- Módulos de funcionalidad (feature modules): funcionalidades o páginas concretas de la aplicación, como 'add-user', 'dashboard', 'login' y 'user-list'.
- Modelos: estructuras de datos que definen la forma de los datos dentro de la aplicación.
- Servicios compartidos: funcionalidades comunes que se usan en distintas partes de la aplicación, como la autenticación (
auth) y la gestión de usuarios (users).
La estructura de directorios es la siguiente:
Estrategia de refactorización
El objetivo es refactorizar los servicios y los modelos en bibliotecas que se puedan compartir entre las micro-apps. Los módulos de funcionalidad, en cambio, se transformarán en aplicaciones individuales. Este enfoque garantiza la modularidad y facilita el desarrollo y el despliegue independientes de cada funcionalidad.
Consideraciones sobre el enrutamiento
La configuración de enrutamiento actual de la aplicación usa lazy loading (carga diferida), lo que mejora el rendimiento y la experiencia de usuario. Este es un extracto que muestra la estrategia de enrutamiento:
Este módulo de enrutamiento muestra el uso de guards (isLogged e isNotLogged) para gestionar el acceso a las distintas rutas según el estado de autenticación del usuario, y de imports dinámicos para la carga diferida de los módulos de funcionalidad.
Empezar con pasos sencillos en la migración a micro-frontends
Enfoque para migrar una aplicación compleja
Migrar una aplicación compleja a una arquitectura de micro-frontends puede ser todo un reto. Para facilitar el proceso, es fundamental contar con pasos claros y, posiblemente, con herramientas adicionales (para gestionar mejor las dependencias).
Primer objetivo: archivos sin dependencias
-
Identifica los archivos independientes: empieza por localizar los archivos que no tienen dependencias pero que se usan en servicios y módulos de funcionalidad. Por ejemplo, en nuestra aplicación de ejemplo, esto se aplica a la carpeta
models, que contiene modelos usados en toda la aplicación pero que no depende de ningún otro archivo. -
Crea una biblioteca nueva para estos archivos: usa Angular CLI para generar una biblioteca que albergue estos archivos independientes. El comando es:
Este comando realiza las siguientes acciones:
- Crea una carpeta
projectsnueva que contiene la bibliotecamodels. - Instala
ng-packagrcomo dependencia. - Actualiza
angular.jsonytsconfig.jsonpara incluir la nueva biblioteca.
Personalizar la biblioteca
- Mapeo de rutas: modifica el mapeo de rutas en
tsconfig.jsonpara evitar posibles conflictos con módulos de npm. Un prefijo recomendado es@@, ya que los nombres de los módulos de npm no pueden incluir dos arrobas. - Nombre del paquete: si piensas publicar la biblioteca en un registro, asegúrate de que el nombre en
package.jsonsea válido para el registro. Si no, puedes elegir el nombre que te resulte más cómodo.
Refactorizar los modelos
- Mueve los modelos: traslada el contenido de la carpeta
modelsa la nueva biblioteca. La biblioteca generada contendrá componentes, módulos y servicios predeterminados, que puedes eliminar. Coloca el archivo del modelo de usuario dentro de la carpetalibde la biblioteca. - Actualiza la API pública: modifica el archivo
public-api.tspara exportar el contenido correspondiente de la carpetalib.
Gestionar el movimiento de archivos
- Usa Git para mover archivos: emplea
git mven lugar de las funciones del sistema operativo para mover archivos. Así Git podrá hacer un mejor seguimiento de los cambios.
Estructura de carpetas de la biblioteca
La estructura de carpetas de la biblioteca models debe organizarse para que sea clara y de fácil acceso.
Actualizar los imports
Cambia las rutas de import: reemplaza las rutas de import existentes por la ruta de la nueva biblioteca. Por ejemplo, cambia:
Asegúrate de que la ruta coincida con la especificada en tsconfig.json.
Compilar la biblioteca
Resolver errores: al principio, es posible que aparezcan errores porque la biblioteca aún no se ha compilado. Para resolverlo, ejecuta:
Este comando compila la biblioteca models, lo que permite que TypeScript haga referencia correctamente a @@models.
Ir más allá de la migración básica
Después de migrar con éxito el código independiente a bibliotecas, el siguiente paso consiste en abordar el código con dependencias que también se pueda aislar. Siguiendo con nuestro ejemplo, nos centraremos en migrar el servicio auth, incluidos sus guards, a una biblioteca independiente. Este proceso replica los pasos seguidos con la biblioteca models.
Crear la biblioteca de autenticación
Generar la biblioteca
Usa Angular CLI para crear la biblioteca auth con el comando:
Refactorizar el contenido de la biblioteca:
Elimina el contenido predeterminado de la carpeta lib y traslada a ella todo el contenido de la carpeta auth. Actualiza el archivo public-api.ts para reflejar estos cambios.
El archivo public-api.ts debería tener un aspecto similar a este:
Compilar la biblioteca
Compila la biblioteca auth con Angular CLI:
Actualizar los imports
Cambia las sentencias import de la aplicación para que usen la nueva biblioteca auth.
¡Enhorabuena! Has migrado con éxito partes de tu aplicación a bibliotecas. Esto no solo mantiene la aplicación en funcionamiento, sino que además puede acelerarla, ya que Angular no necesita recompilar toda la aplicación. Ahora tienes varias bibliotecas con pruebas y builds aislados, algo adecuado para aplicaciones grandes en las que se comparten componentes, servicios, pipes y otros elementos.
Organización avanzada de bibliotecas
En aplicaciones más complejas, plantéate usar la funcionalidad Secondary Entrypoints de ng-packagr para agrupar elementos comunes, como componentes o servicios. Esto requiere ajustes de configuración adicionales.
Migrar la primera aplicación
Ahora que sabes empaquetar bibliotecas, pasamos a migrar un módulo de funcionalidad a su propia aplicación.
Elegir un módulo para migrar
En nuestro ejemplo, se elige el módulo de funcionalidad Login para la migración. Asegúrate de que todos los artefactos compartidos que usa este módulo ya estén en sus respectivas bibliotecas. Otros servicios, como el servicio users, se pueden migrar más adelante.
Crear la aplicación de login
Generar la aplicación
Usa Angular CLI para crear la aplicación login:
El flag --routing es imprescindible para la navegación dentro de la aplicación.
Servir la aplicación
Prueba la aplicación recién creada con:
Si la aplicación principal está en ejecución, es posible que tengas que usar otro puerto.
Refactorizar el módulo de funcionalidad
-
Traslada el módulo de funcionalidad: en lugar de reemplazar archivos como hiciste con las bibliotecas, crea una carpeta
featureen elsrcde la nueva aplicación y mueve a ella la carpetalogin. -
Actualiza el enrutamiento: modifica el
app-routing.module.tsde la aplicaciónloginpara servir el móduloLoginen la ruta raíz. -
Ajusta el AppComponent: asegúrate de que el
app.component.htmlde la aplicaciónlogincontenga una etiqueta<router-outlet></router-outlet>para que el enrutamiento funcione correctamente.
Gestionar los estilos
Al principio, puede que la nueva aplicación aparezca sin estilos. Para resolverlo, copia el archivo styles.scss de la aplicación principal a la nueva. En próximas publicaciones profundizaremos en cómo compartir estilos entre aplicaciones.
Resultado
Si lo has hecho correctamente, la aplicación login debería ejecutarse ahora de forma independiente, con estilos y funcional, lo que supone un paso importante en tu camino hacia una arquitectura de micro-frontends completamente implementada.
Integrar la aplicación de login en la aplicación principal
Después de configurar la aplicación Login como una entidad independiente, el siguiente paso crítico es integrarla con la aplicación principal. Para ello, se compila la aplicación Login como una biblioteca y se importa usando la funcionalidad de carga diferida existente en el módulo principal.
Actualizar angular.json
- Modifica la configuración del proyecto: en
angular.json, duplica la configuración de una biblioteca y colócala bajo la configuración deloginpara una mejor organización. Cambia el nombre de la clave alogin-liby actualiza las referencias (comorootysourceRoot) para que apunten a la aplicaciónlogin.
La configuración actualizada debe ajustarse para reflejar las rutas y los ajustes específicos de la aplicación login.
+
2. Crea ng-package.json: este archivo es esencial para indicar a ng-packagr cómo empaquetar la aplicación. Copia un archivo ng-package.json existente de otra biblioteca en la carpeta raíz de la aplicación Login. Actualiza la clave dest a "../../dist/login-lib" para especificar el directorio de salida del build.
Establecer la API pública
- Crea
public-api.ts: a diferencia de las configuraciones habituales de bibliotecas, este archivo debe crearse en la carpetasrc. Solo debe exportar el módulo de funcionalidadLogin:
Compilar la biblioteca
- Prepara
package.json: copia unpackage.jsonde otra biblioteca en la raíz de la aplicaciónLoginy asegúrate de actualizar el nombre del proyecto. - Comando de build: ejecuta el comando de build de Angular CLI:
Este proceso compila el módulo Login como una biblioteca.
Importar la biblioteca
-
Actualiza
tsconfig.json: añade un nuevo mapeo de rutas paralogin-libentsconfig.jsonque apunte a"dist/login-lib". Este paso es crucial para que TypeScript localice correctamente la biblioteca. -
Modifica el enrutamiento de la aplicación: en
app-routing.module.ts, actualiza la ruta de carga diferida para usar la nueva biblioteca:
Ejecutar la aplicación integrada
Con estos cambios, puedes servir la aplicación como de costumbre. El módulo Login, ahora un proyecto independiente, debería funcionar sin problemas dentro de la aplicación principal.
Otras oportunidades y consideraciones
- Automatizar los builds: para agilizar el proceso, plantéate usar herramientas como Lerna para automatizar los builds y la gestión de dependencias. Esto puede ayudar especialmente cuando trabajas con varias bibliotecas y aplicaciones.
- Gestión de versiones: actualmente, la configuración no usa versionado para las bibliotecas, lo que podría complicar la introducción de cambios incompatibles. Implementar una estrategia de versionado podría ser beneficioso para el mantenimiento a largo plazo.