Introducción a los Micro Frontends

En esta primera entrada de blog sobre nuestra nueva serie de Micro Frontends, exploramos su definición y analizamos las ventajas y desafíos asociados. Además, abordamos las razones para adoptar micro frontends, diversos patrones de implementación, trade-offs importantes y, para concluir, compartimos ejemplos de su implementación con algunos de nuestros clientes.

¿Qué son los micro frontends?

En términos generales, los micro frontends representan un estilo arquitectónico de User Interface (UI) en el cual una única interfaz se divide en componentes más pequeños, cada uno desplegable de manera independiente. Este enfoque resulta beneficioso para agilizar la implementación de cambios en el software y aislar cualquier modificación.

Contexto

El término "micro frontends" toma su nombre de los "microservicios", que han ganado mucha popularidad en los últimos años. La característica clave de la arquitectura de microservicios es que sus componentes son independientes y de bajo acoplamiento, formando así un sistema más grande donde cada uno puede desplegarse de manera independiente. Siguiendo la misma definición, podemos considerar las propiedades esenciales de los micro frontends como componentes frontend de acoplamiento débil e independientes entre sí, que se integran para formar una interfaz de usuario más extensa.

Cómo los micro frontends ayudan a desacoplar partes de una UI

Cuando se diseñan sistemas de software, es crucial tener en cuenta el grado de acoplamiento entre los componentes, ya que un acoplamiento elevado dificulta que varios equipos trabajen de forma autónoma. Esto conduce a la necesidad de una mayor coordinación entre equipos, aumentando así la complejidad organizativa, ralentizando la velocidad de entrega y aumentando la carga cognitiva para los mismos.

Una de las áreas donde más frecuentemente se detecta un acoplamiento elevado es la interfaz de usuario. Esta es la capa donde se consolidan los datos para ser presentados a los usuarios de manera útil y significativa.

Los micro frontends buscan reducir este nivel de acoplamiento entre los componentes de una interfaz de usuario, permitiendo así que las diferentes partes pueden evolucionar separadamente y tener implementaciones independientes. A su vez, los micro frontends pueden reducir el acoplamiento entre equipos. Es decir, al descomponer una interfaz de usuario en varios componentes más pequeños, se pueden dividir adecuadamente la propiedad de los mismos. Al hacerlo, se eliminan dependencias entre equipos como las que se dan al alinear despliegues y secuenciaciones de trabajo, o al acordar contratos para el intercambio de datos entre sistemas que cruzan los límites de los equipos. 

Beneficios de los micro frontends

1. Flexibilidad en la elección de la tecnología

Uno de los posibles beneficios de adoptar una estrategia de micro frontends es que ofrece la opción de utilizar múltiples frameworks. Aunque a veces se considera un anti-patrón debido a que diferentes frameworks no están inherentemente diseñados para ser inicializados juntos en la misma pestaña del navegador, existen situaciones en las que hacer esto sea necesario. Algunas de estas situaciones son al trabajar con legacy code, migrar a un nuevo framework/libreria frontend o al integrar dos sistemas dispares (por ejemplo, como resultado de una adquisición o fusión empresarial). Contar con la flexibilidad de combinar varios frameworks en estas situaciones puede ser beneficioso.

2. Despliegues independientes

Otro beneficio al adoptar micro frontends es que ofrecen la opción de realizar despliegues independientes. En otras palabras, cada micro frontend puede ser desplegado de manera individual. Esto conlleva a una reducción en el alcance de cada despliegue en particular, donde los despliegues se vuelven intrínsecamente menos arriesgados al introducir menos cambios en cada uno.

Figure 1

Como ocurre con los microservicios, cada micro frontend puede y debe tener su propio delivery pipeline continuo. Un mecanismo automatizado para su construcción, prueba y despliegue posterior en todos los entornos de prueba disponibles y en producción. Los equipos que poseen un micro frontend no deberían tener que consultar con los owners de otros micro frontends para ponerse de acuerdo en los despliegues. Cada micro frontend puede ser desplegado tan pronto como esté listo para producción y a un ritmo elegido por el equipo que lo posee. Esto elimina la necesidad de una coordinación costosa entre equipos y garantiza que los cambios puedan desplegarse en producción de manera más oportuna (en lugar de tener que adherirse a un ciclo de lanzamiento fijo que se alinee a otros equipos).

3. Codebases más pequeñas

El codebase de cualquier micro frontend será, debido a su naturaleza, considerablemente más pequeña que la de una aplicación monolítica o un frontend convencional. Esta reducción en tamaño aligera la carga cognitiva para los desarrolladores que deben trabajar en ella, facilitando su gestión y la realización de cambios. Además, separar estos micro frontends en sus propias bases de código también brinda un nivel de protección contra acoplamientos no deseados. Por ejemplo, hace que sea menos probable que los equipos compartan modelos de dominio entre distintos codebases.

4. Equipos Autónomos

Quizás uno de los beneficios más estratégicos de adoptar micro frontends e introducir despliegues y bases de código independientes es que permite tener equipos capaces de funcionar de manera autónoma. Esto significa que los equipos pueden poseer un slice vertical entero desde la interfaz de usuario hasta la persistencia de datos. Aquí, 'poseer' incluye tanto la definición del trabajo que debe realizarse hasta el despliegue de ese trabajo en un entorno de producción. Es probable que se necesiten cambios arquitectónicos y de procesos adicionales para realizar esto completamente, pero adoptar micro frontends proporciona muchas de las piezas fundamentales necesarias.

Contar con equipos autónomos permite un mayor nivel de agilidad. Esto se debe a que tener plena propiedad de un slice vertical permite a los equipos moverse rápidamente, sin el costo de tener que coordinarse con otros equipos respecto a roadmaps, listas de tasks y despliegues. Sin embargo, para lograr este nivel de autonomía, los equipos deben formarse en torno a slices verticales de funcionalidades de negocio y no en torno a preocupaciones técnicas. Por ejemplo, establecer un equipo de pagos en lugar de un equipo de UI. En su libro Team Topologies, Matthew Skelton y Manuel Pais se refieren a esto como "equipos alineados con el flujo", describiendo estos tipos de equipos como "alineados con un único y valioso flujo de trabajo".

Desafíos de los micro frontends

A pesar de que los micro frontends pueden proporcionar numerosos beneficios, como ocurre con la mayoría de las cosas en la vida, no vienen de forma gratuita. Existe una complejidad adicional al dividir el frontend en piezas más pequeñas y, si no se gestiona adecuadamente, esta complejidad puede contrarrestar los beneficios.

1. Lograr la granularidad adecuada

La fragmentación de micro frontends puede ser demasiado granular o excesivamente grande. Existe un esfuerzo asociado con la división de los micro frontends, especialmente cuando hay un cambio en la propiedad. Como con cualquier límite de componente, resulta útil observarlos desde la perspectiva de acoplamiento y cohesión. La granularidad tiene un impacto directo en la complejidad de la comunicación entre los componentes, y los componentes con patrones de comunicación complejos que atraviesan los límites del equipo requerirían relaciones más intrincadas entre esos equipos.

Por ejemplo, un método para dividir micro frontends es a través del patrón de Orquestador. Discutiremos esto más a fondo en un artículo posterior, pero en pocas palabras, un Orquestador sería un contenedor o página que alberga diferentes componentes de micro frontend.

Existen decisiones que deben tomarse a nivel técnico y organizativo sobre si construir y mantener un componente orquestador para alojar otros micro frontends o si esa responsabilidad debería recaer en el componente principal de la página. Si se opta por un orquestador, la decisión sobre qué equipo o equipos deben ser responsables de él es una elección que merece ser tomada de manera conjunta y revisada periódicamente.

2. Propiedad del equipo

La orquestación entre equipos es un punto importante a considerar. Es bastante fácil que las responsabilidades se filtren a otros contextos si la integridad de esos micro frontends no está bien regulada.

Por ejemplo, consideremos una empresa de e-commerce que tiene equipos relacionados con la lista de productos y finanzas. Cada uno produce componentes de frontend para el sitio web. La Lista de productos solo necesita ocuparse de mostrar información sobre los detalles del producto y, por lo tanto, debería dejar áreas de la interfaz de usuario disponibles para que otros equipos, como finanzas en este ejemplo, llenen con su(s) componente(s) de frontend. Esto requiere una coordinación cuidadosa entre los equipos para que las responsabilidades sobre qué mostrar en una página estén bien gestionadas y comprendidas.

3. Configuración local

Es crucial contemplar cómo los desarrolladores pueden visualizar toda la aplicación en sus máquinas locales. En configuraciones menos deseables, los desarrolladores se ven obligados a utilizar un entorno de prueba separado para observar sus cambios en el contexto de toda la aplicación. Esto debería evitarse. La configuración local es vital para la productividad del desarrollador y debería facilitar la configuración sin conexión tanto como sea posible.

Dependiendo del tipo de enfoque de integración, se puede construir un entorno para contener su(s) micro frontend(s) y simular (o falsificar) sus dependencias externas. Esto puede ser un entorno eficiente para el desarrollo local.

4. Implementaciones complejas

A menudo, las organizaciones y los equipos de desarrollo de software que adoptan una arquitectura de microservicios por primera vez no comprenden completamente los trade-offs implicados. Un trade-off principal a que ver con el despliegue y la orquestación. Lo mismo ocurre al adoptar micro frontends. Al igual que los microservicios, los micro frontends requieren mucha más madurez técnica en torno a los procesos de implementación.

Con la interfaz de usuario monolítica más convencional, solo se necesita un proceso de implementación para desplegar. Al adoptar una arquitectura de micro frontends, ahora se requieren procesos de implementación individuales para cada componente de micro frontend. Además, estos procesos deben estar desacoplados entre sí para permitir diferentes cadencias de implementación y posiblemente incluso ownership por equipos diferentes. Para lograr esto, es necesario comprender muy bien las dependencias entre cada módulo de micro frontend.

Todo esto significa que, para tener la confianza suficiente de que un cambio en un micro frontend es seguro para ir a producción, necesitamos invertir en asegurarnos de tener la mayor automatización posible, pero también el nivel correcto de pruebas dentro de los procesos de implementación.

Si bien estas cosas pueden ser dificiles de manejar, especialmente al tratar con ellas por primera vez, es bueno reconocer los amplios beneficios que provienen de satisfacer las demandas de este patrón. Los micro frontends fomentan una cultura de desarrollo madura y responsable donde los equipos pueden disfrutar de autonomía, tener mayor ownership sobre lo que están construyendo o entregando a los clientes, pero en la cual se les exige comprender su aplicación en relación con el sistema más amplio.

5. Comunicación compleja entre componentes

Cuando la comunicación entre diferentes micro frontends se vuelve compleja, al igual que con cualquier otro software, vale la pena reflexionar sobre el nivel de acoplamiento y cohesión existente entre esos componentes. Discutiremos el tema de cómo manejar cambios en los límites de los componentes en un artículo posterior de esta serie. Por ahora, destaquemos un par de situaciones donde puede surgir una comunicación compleja entre componentes de micro frontend en la capa del UI:

  • Cuando un componente que se integra durante el tiempo de compilación podría ser iniciado con información que solo puede provenir del componente que lo hospeda, durante el tiempo de ejecución.
  • Cuando una página contiene múltiples componentes que comparten un estado común, cada componente proporciona al usuario una forma de actualizar ese estado. La manera en que una aplicación orquesta dicho flujo de datos puede tener implicaciones que afectan seriamente el rendimiento de la experiencia de la página, lo cual puede perjudicar la experiencia general del usuario.


Debe existir una estrategia clara sobre cómo los componentes de micro frontends se comunicarán entre sí. Esta deberá adaptarse a todos los tipos diferentes de componentes que puedan existir dentro de su arquitectura de micro frontends. Ya sea que los componentes se integren durante la compilación, implementación o tiempo de ejecución, las entradas y salidas disponibles deben estar claramente definidas. En otras palabras, cada componente ofrece una API que permite que otros componentes interactúen con él.

Otro concepto sensato para manejar la comunicación compleja es el orquestador. Un orquestador simple podría tomar la forma de un componente principal que hospede varios hijos. Otros orquestadores definen relaciones más complejas y pueden gestionar el intercambio de datos entre múltiples micro frontends.

6. Estilo consistente

Independientemente del patrón arquitectónico de frontend elegido, este debe ser invisible a los usuarios. Queremos que nuestros usuarios sientan que están interactuando con una sola aplicación, aunque en realidad pueda estar compuesta por múltiples componentes desarrollados de manera independiente. Una parte clave para lograr esto es al ofrecer un estilo y una experiencia de usuario consistentes en todo el sistema. Sin embargo, componer una sola aplicación a partir de múltiples repositorios de código puede hacer que esto sea un desafío.

Adoptar un sistema de diseño en toda la aplicación es una forma de remediar esto. Un sistema de diseño generalmente define estilos y controles de usuario a nivel atómico que pueden ser compartidos y reutilizados para cualquier componente. Cuando se realiza un cambio de estilo, este debería actualizarse de manera consistente en toda la aplicación, ya que cada componente que consume el sistema de diseño se actualiza para utilizar la última versión. Se debe tener cuidado al planificar este tipo de colaboración. No obstante, al igual que con muchos de los desafíos de micro frontends, las soluciones han estado disponibles durante mucho tiempo y serán familiares para equipos avanzados. En este caso, la versión semántica, las notas de lanzamiento consideradas y una buena comunicación entre equipos hacen toda la diferencia.

Además, debe existir una estrategia clara sobre cómo el estilo de la interfaz de usuario (es decir, el CSS) será gestionado por los componentes de micro frontends, especialmente cuando se trata de estilos globales (estilos en toda la aplicación) y estilos locales (estilos para un componente de micro frontend individual). Esto es para evitar que las reglas CSS se anulen entre sí, lo que puede tener un efecto perjudicial tanto en la experiencia del usuario como en la del desarrollador.

7. Estandarización del enfoque de desarrollo

Si bien adoptar una arquitectura de micro frontends puede permitir que los equipos individuales tengan un mayor nivel de autonomía, cierto grado de gobernanza y estandarización del enfoque de desarrollo puede ser altamente beneficioso. Sin esto, pueden surgir ciertos problemas. Por ejemplo, con equipos ahora libres de elegir sus propias tecnologías de frontend, se podrían crear silos de conocimiento en toda una organización. De manera similar, si diferentes equipos de ingeniería han adoptado muchas tecnologías diferentes, puede dificultar mucho que las personas se muevan entre equipos o proyectos. Además, a lo largo de este mayor nivel de autonomía, los equipos ahora desarrollarían componentes de frontend de manera aislada. Si bien esto tiene beneficios claros, puede llevar a expectativas diferentes en torno a enfoques de desarrollo (como convenciones de codificación, estándares de calidad, etc). Aunque un cierto grado de autonomía es bueno, debe existir un nivel de gobernanza para asegurar que la calidad en los equipos se mantenga y que ciertos enfoques estén estandarizados.

Algunas soluciones que pueden funcionar bien aquí son adoptar herramientas de análisis de código estático y tener un estándar común para su configuración. Uno de nuestros clientes, Cazoo, creó un paquete compartido que definía un conjunto de reglas de linting que podían incorporarse en diferentes bases de código en toda la organización. Esto aseguraba consistencia en los estilos de coding y evitaba la duplicación de reglas de linting. Contar con una clara estrategia tecnológica en toda la organización también puede proporcionar barandillas para que los equipos de desarrollo se aseguren de que están siguiendo las elecciones tecnológicas correctas, pero también tengan un grado de autonomía para tomar ciertos tipos de decisiones por sí mismos. Finalmente, con equipos más autónomos, es importante crear plataformas para compartir conocimientos. Las Comunidades de Práctica internas a menudo funcionan bien para esto.

8. Testing entre componentes

Una arquitectura de micro frontends permite a los equipos testear sus componentes exhaustivamente de manera aislada de otros componentes. Los equipos pueden elegir la estrategia y los marcos de prueba de manera independiente entre sí para que se adapten mejor a sus necesidades específicas. En la mayoría de los casos, una arquitectura de micro frontends simplifica los tests, ya que es más fácil probar módulos pequeños de manera aislada que un único monolito grande.

La complejidad principal reside en asegurarse de que los micro frontends desarrollados e implementados de manera independiente funcionen perfectamente juntos. En un escenario ideal, cada micro frontend debería contar con su propia suite completa de pruebas de automatización. Sin embargo, dado que estas pruebas solo verifican el comportamiento de un micro frontend individual, es necesario agregar algún nivel de pruebas para verificar la integración de todos los componentes.

Una estrategia para testear las integraciones consiste en emplear 'Pruebas de Contrato'. Esta metodología permite enfocarnos estrictamente en probar los límites de la interfaz (o "contratos") entre los micro frontends. La ventaja de este tipo de pruebas es que son bastante simples y rápidas de ejecutar. Además, ayuda a los equipos a mantener una interfaz limpia y visible entre su micro frontend y el de los demás. De esta manera, estas pruebas aseguran que se cumpla el contrato de interfaz entre los micro frontends.

Otro enfoque consiste en crear pruebas automatizadas de extremo a extremo. Las principales preocupaciones con esta solución son que este tipo de pruebas suelen ser de larga duración, complejas, a menudo frágiles y costosas de mantener. Si se utilizan pruebas de extremo a extremo, deberían centrarse en validar que la aplicación web integrada funcione correctamente.

9. Problemas de seguridad

Al igual que con cualquier otro patrón arquitectónico, la seguridad debe ser cuidadosamente considerada, y los micro frontends no son una excepción. De hecho, los micro frontends introducen algunos desafíos de seguridad específicos que deben ser entendidos antes de ser adoptados.

En primer lugar, una arquitectura de micro frontends requiere que cada componente individual sea seguro. Si algún componente contuviera una vulnerabilidad de seguridad, podría comprometer toda la aplicación. Esto significa que cada equipo individual que trabaja en un componente debe asegurarse de que el mismo sea seguro.

De manera similar, es crucial prestar atención a la forma en que interactúan entre sí los componentes de los micro frontends, en particular, cómo comparten datos y recursos. Se deben establecer mecanismos de seguridad adecuados para asegurar que solo los componentes autorizados puedan acceder a datos y recursos de otros componentes, y que no se exponga información sensible a otros componentes o al navegador cuando no debería.

El Compartir Recursos de Origen Cruzado (CORS, por sus siglas en inglés) también puede ser un punto de preocupación en una arquitectura de micro frontends si los componentes individuales se sirven desde dominios diferentes. Los navegadores web, de manera justificada, lo evitan por defecto. En este escenario, es necesario configurar los encabezados y políticas CORS adecuados para permitir específicamente estas integraciones.

10. Librerías y dependencias de distintos niveles y versiones

Otro de los inconvenientes de adoptar micro frontends es la complejidad adicional que surge al gestionar librerías y dependencias. Es común que los micro frontends dentro de un proyecto utilicen las mismas librerías principales, como por ejemplo React o Redux. Estas librerías pueden ser agrupadas en el código de producción del componente de micro frontend.

Debido a esto, la cantidad de datos que el usuario final necesita descargar es mucho mayor que en una arquitectura monolítica. Esto puede afectar el rendimiento y provocar una experiencia de usuario deficiente. Module Federation de Webpack proporciona una solución a esto al ofrecer una manera de compartir dependencias comunes entre varios "módulos".

Con los navegadores modernos admitiendo ahora módulos nativos de JavaScript, existe otra opción disponible. Es posible referenciar dependencias de terceros desde una red de entrega de contenido (CDN, por sus siglas en inglés) pública, lo que proporciona el beneficio adicional de que el navegador puede utilizar una versión en caché de las dependencias si se cargaron previamente para cualquier sitio web.

Otro problema surge cuando los micro frontends tienen dependencias con diferentes versiones de las mismas librerías. Esto añade un nivel de complejidad y puede llevar incluso a situaciones donde el sistema deja de funcionar debido al uso de versiones incompatibles. Algunas de las soluciones mencionadas anteriormente para estandarizar enfoques de desarrollo también son relevantes aquí. Como con muchos de los problemas, las soluciones tienen sus propios beneficios: equipos avanzados, propiedad responsable y buena comunicación.

Otro potencial problema es que los diferentes componentes de micro frontends podrían estar utilizando diferentes librerías o diferentes versiones de la misma librería. Esto abre un riesgo potencial de seguridad. Es vital tener una política de gobernanza en torno al uso de librerías y frameworks de terceros para rastrear qué versiones se están utilizando y tener un proceso para actualizarlas cuando sea necesario. Afortunadamente, existen herramientas automatizadas como Dependabot y Synk que cruzan las versiones de dependencias con listas de vulnerabilidades conocidas y pueden eliminar gran parte del trabajo manual para mantener actualizadas las librerías de terceros.

11. La constante evolución de los micro frontends

A pesar de su creciente popularidad en los últimos años, este patrón arquitectónico sigue siendo relativamente nuevo. Como resultado, aún se está realizando mucha investigación sobre el tema y muchas prácticas aún están por surgir y ser establecidas.

Dado su crecimiento reciente en popularidad y su relativa falta de madurez en comparación con algunos patrones existentes, ha habido numerosos enfoques para implementar micro frontends. Esto puede generar desafíos para equipos y organizaciones que desean adoptar micro frontends por primera vez y buscan orientación en la industria. Una situación similar ocurrió, por ejemplo, cuando surgió el patrón de arquitectura de microservicios.

Al igual que con cualquier tecnología o patrón nuevo, el desarrollo y la madurez de las herramientas y el soporte llevan tiempo. Lo mismo sucede con los micro frontends. Por ejemplo, se suele mencionar que hay una falta de herramientas para la comunicación de enrutamiento entre los componentes de micro frontends. Como resultado, los equipos, con mayor frecuencia, necesitan construir esto internamente.

Aunque el panorama está evolucionando rápidamente, esto no es algo nuevo para JavaScript, y se está consolidando una cierta estabilidad en el ámbito de los micro frontends. Estrategias como la arquitectura de islas y el patrón del orquestador, que discutiremos en un artículo futuro, nos proporcionan formas más sólidas de pensar en la arquitectura del frontend. Con los módulos nativos ahora disponibles en los navegadores modernos, la necesidad de agrupar JavaScript ha desaparecido. Los micro frontends pueden compartir dependencias tan fácilmente como referenciar el mismo enlace, por lo que uno de los principales desafíos ya no es un problema. En general, la rápida madurez de las herramientas modernas de desarrollo web (por ejemplo, JAMStack) está simplificando cada vez más el diseño de arquitecturas de software (incluidos los micro frontends) que se ajustan a las necesidades específicas de una organización. Estas se construyen sobre patrones y soluciones familiares que han existido mucho más tiempo que los propios micro frontends.

Reflexiones finales

Este artículo ha buscado ofrecer una introducción a los micro frontends como un patrón de arquitectura frontend. Al tomar decisiones que impactan la arquitectura de un sistema de software, es crucial considerar los trade-offs específicos de su propia situación. Diferentes equipos y organizaciones tendrán sus propias necesidades arquitectónicas, por lo que adoptar micro frontends puede ser una buena elección para algunos, pero no necesariamente para todas las organizaciones. A lo largo del artículo, hemos destacado lo que consideramos algunos de los principales beneficios y desafíos de los micro frontends, y hemos abordado soluciones para algunos de los mismos. En resumen, aquí facilitamos un desglose:


Beneficios de los micro frontends:

  • Flexibilidad en la elección de la tecnología
  • Despliegues independientes
  • Bases de código más pequeñas
  • Equipos autónomos

 

Desafíos de los micro frontends:
  • La comunicación entre componentes puede ser compleja
  • Estilo consistente
  • Constante evolución de los micro frontends
  • Los límites de dominio son más costosos de cambiar
  • Consideraciones adicionales en torno al despliegue
  • Compartir librerías y dependencias en diferentes niveles
  • La configuración local requiere trabajo adicional
  • Desafíos de seguridad más amplios
  • Estandarización del enfoque de desarrollo
  • Pruebas entre componentes


Colaboradores

Este artículo fue escrito colaborativamente por Mashooq Badar, Matt Belcher, Daniel Bird, Laurence Lord, Krzysztof Napierala y Sandro Viljoen.

New call-to-action