Llevar a cabo un buen desarrollo frontend a medida que este va creciendo puede resultar, en muchos casos, un gran reto para el equipo involucrado. Es posible que si el producto en el que participamos avanza con rapidez, nos parezca cada vez más difícil escalar el desarrollo, permitiendo a su vez que todos los equipos sigan trabajando con el ritmo y la calidad habitual.

En este artículo te explicaré la idea de los microfrontends con el fin de dividir nuestra interfaz, con un enfoque monolítico, en diferentes módulos por funcionalidades e implementarlos de manera independiente (tal y como sucede con los microservicios en la parte del backend). Comentaremos las ventajas y desventajas de esta arquitectura enseñando a su vez una aplicación de ejemplo para entender visualmente el concepto.

¿Qué son los microfrontends?

El término microfrontends surge por primera vez en 2016 en ThoughtWorks Technology Radar, una guía sobre las tecnologías de vanguardia, extendiendo el concepto de los microservicios del backend hacia el lado del frontend. Desde entonces, el término se ha utilizado ampliamente en la comunidad de desarrollo web y se ha convertido en un tema de interés para muchos desarrolladores y empresas que buscan mejorar la escalabilidad, la modularidad y la flexibilidad de sus aplicaciones.

En las aplicaciones web de hoy día, el frontend adquiere cada vez más importancia y el enfoque de un monolito tradicional termina por hacerse complejo de gestionar y mantener en el largo plazo. Es por este motivo que, a partir de aplicar la arquitectura de microservicios en el backend dividiendo el desarrollo en servicios independientes, se opta por aplicar este mismo patrón en la parte del frontend tratando de componer la interfaz mediante distintos módulos o aplicaciones especializadas en funcionalidades concretas.

En la siguiente imagen podemos distinguir la evolución de las distintas arquitecturas, desde un monolito compacto sin capas intermedias involucradas, hasta una arquitectura de microservicios en la parte del backend combinada con los microfrontends en su parte frontend.

Evolución desde un monolito compacto hasta una arquitectura de microservicios.

La idea principal tras la arquitectura de microfrontends es concebir el sitio web como un conjunto de aplicaciones independientes, cada una de las cuales se desarrolla y despliega de forma autónoma por su respectivo equipo de desarrollo.

Como resultado, cada equipo forma parte de un área de negocio y desarrolla su funcionalidad de manera independiente al resto y sin dependencias externas. Esto permite una mayor flexibilidad y reduce el acoplamiento entre los diferentes módulos o aplicaciones que componen el sitio.

Martin Fowler, reconocido autor e ingeniero de software, describe esta técnica de la siguiente manera: “un estilo arquitectónico en el que aplicaciones frontales independientes se integran en un conjunto mayor”.

Este enfoque en la organización de los equipos puede resultar especialmente útil en proyectos grandes con muchos desarrolladores involucrados, puesto que al dividir los equipos en distintas funcionalidades evitamos la constante comunicación y coordinación entre los mismos.

Ventajas de los microfrontends

Ejemplo paso a paso

Imaginemos una aplicación web que tiene por objetivo la venta de zapatillas multimarca. A priori, la idea está bastante clara por las personas implicadas, todos ellos son buenos desarrolladores, pero carecen de experiencia implementando arquitecturas de microfrontends.

Por este motivo, deciden empezar realizando una prueba de concepto que reúna lo mínimo necesario para poder comprar una zapatilla de manera ágil y sencilla, con el único requisito de que los elementos de la página estén organizados en distintos microfrontends clasificados por funcionalidad.

Teniendo esto en cuenta, plantean la necesidad de tener 3 microfrontends como mínimo para poder realizar un primer proceso de compra sencillo, todos ellos conviviendo en la misma homepage, ya que por el momento no se quiere que la aplicación tenga secciones o un menú para evitar gestionar rutas y simplificar al máximo.

En un prototipo sencillo llegan al siguiente acuerdo:

Prototipo con opción de búsqueda, listado y carrito.

Para una primera aproximación de la prueba de concepto, podemos asegurar que hay suficiente complejidad en la homepage como para repartir cada microfrontend a un pequeño equipo de desarrolladores dedicado exclusivamente a esa funcionalidad específica.

Cada microfrontend podrá ser desarrollado, probado y desplegado por cada equipo con total autonomía, agilizando así la puesta en marcha de una primera demo que poder enseñar a los interesados.

Esquema con los tres microfonrtes: búsqueda, listado y carrito

De este modo, cada equipo podrá trabajar únicamente en su microfrontend, ejecutado y desplegado de forma individual, sin preocuparse por los conflictos o la coordinación con otros equipos paralelos. Sin embargo, de cara a nuestro cliente o persona interesada, esto resultará completamente transparente, ya que el producto final es un sitio web completo sin modificación alguna: el microfrontend contenedor (el host).

Con esta manera de organizarse y gestionar sus propios desarrollos, los equipos pasan a adoptar una organización vertical, un patrón muy común en este tipo de arquitectura mediante el cual cada equipo asume la responsabilidad de crear su respectiva parte de la interfaz y de tener en cuenta el resto de capas técnicas involucradas para hacerlo posible.

Todo ello sobre la base de un objetivo común del equipo y directamente relacionado con su funcionalidad específica dentro de la homepage.

Lo tres equipos se organizan de manera vertical

Con estos objetivos individuales, cada equipo irá más allá en el desarrollo de las partes de la aplicación y no se centrarán únicamente en escribir las líneas de código necesarias para su funcionamiento. Adoptarán una visión global en cada microfrontend como si se tratara de una aplicación completa, teniendo en cuenta otros aspectos relevantes tales como: presentar una interfaz con buen rendimiento, tener en cuenta la accesibilidad, proporcionar rapidez en la carga de los contenidos, mantener consistencia en los estilos, etc.

Con el prototipo como referencia y teniendo claro la distribución de las funcionalidades, se ponen manos a la obra y despliegan una primera versión de la prueba de concepto de la tienda de zapatillas:

Primera versión de la prueba de concepto

En la siguiente imagen podemos distinguir el espacio reservado para cada uno de los microfrontends de la homepage de la misma manera que se representaba en el prototipo. Los distintos equipos trabajarán en cada área de manera independiente para ofrecer una buena escalabilidad a futuro y agilidad en el desarrollo global de la aplicación.

Cada área sombreada de manera independiente.

Detalles técnicos

Para implementar este tipo de arquitectura, se ha optado por utilizar la técnica más extendida y utilizada hoy día: el plugin Module Federation que nos ofrece Webpack en su versión 5. Para componer la interfaz, hemos usado React como librería única para todos los microfrontends.

El enfoque principal de Module Federation es la creación de módulos remotos, entendidos como piezas de código independientes y compartidas entre diferentes aplicaciones. Cada aplicación puede cargar estos módulos remotos de forma dinámica durante el tiempo de ejecución, lo que permite compartir funcionalidades y recursos entre ellas.

Para implementar este plugin se llevan a cabo las distintas configuraciones y conceptos a tener en cuenta:

  1. Configuración de los módulos remotos: definición de aquellos módulos o partes de la interfaz que deben ser expuestas para ser utilizados por otros.
    En nuestro caso, todas las partes de la interfaz definidas en el prototipo son distintas aplicaciones remotas que se exponen para ser utilizadas: búsqueda y filtrado de productos, listado de productos y carrito de la compra.
    En la propiedad exposes indicamos los componentes que queremos exponer del microfrontend en el que nos encontramos para poder ser utilizado por otros.
Ejemplo de configuración del microfrontend de búsqueda de productos.
Ejemplo de configuración del microfrontend de búsqueda de productos.
  1. Configuración del módulo contenedor (host): definición del microfrontend que va a contener al resto de módulos remotos.
    En nuestro caso, el módulo contenedor será el microfrontend dedicado a la homepage, que importará el resto de microfrontends de cada funcionalidad y únicamente expondrá una store para la gestión del estado global de los microfrontends.
Ejemplo de configuración del microfrontend de homepage.
Ejemplo de configuración del microfrontend de homepage.

De esta manera, podemos importar todos los microfrontends y los componentes que hayamos expuesto desde ellos, como si de un módulo se tratara, tal y como los hayamos nombrado en nuestra configuración:

Importamos todos los microfrontends
  1. Configuración de librerías o dependencias compartidas: definición de aquellas dependencias o librerías que queramos compartir entre módulos, incluidas en la propiedad shared de la configuración.

En nuestro ejemplo, todos los microfrontends indican que quieren compartir la librería de React para hacer posible la comunicación entre ellos. En próximas versiones, podríamos necesitar la inclusión de nuevas dependencias más específicas. Por ejemplo, en el microfrontend dedicado al carrito de la compra, la librería Stripe para poder procesar los pagos de las zapatillas.

Los módulos federados de Webpack nos facilitan el poder compartir información y recursos entre aplicaciones independientes, mejorando la modularidad, la reutilización de código y la flexibilidad en el desarrollo de aplicaciones web.

Con unas pocas líneas de código en cada uno de los ficheros de configuración de Webpack, ya tendremos fácilmente implementada nuestra arquitectura de microfrontends.

Desventajas

Buenas prácticas y aspectos a tener en cuenta

Vistas las ventajas y desventajas de esta arquitectura, vamos a enumerar un listado de buenas prácticas o aspectos a tener en cuenta antes de lanzarnos a su implementación en nuestro equipo:

  1. Limitar la libertad de cada microfrontend: evitar caer en el libertinaje de que los equipos puedan usar sus propios frameworks o librerías (Angular, Vue, React, LitElement, Svelte…). Sobre el papel resulta atractivo este nivel de libertad, pero en la práctica nos puede provocar muchos problemas, puesto que cada microfrontend requerirá sus propias dependencias para ejecutarse.
    El uso de una misma tecnología en todos los microfrontends nos ayudará a optimizar el tamaño final de nuestra aplicación, así como a evitar que aspectos relevantes (como el rendimiento y la experiencia de usuario) no se vean afectados por la ejecución de distintas tecnologías en paralelo.
  2. Valorar la necesidad de implementar microfrontends: tener claro si esta arquitectura realmente nos puede resultar útil en función de los equipos que queramos definir y sus objetivos o, por el contrario, nos basta con componentizar la interfaz sin necesidad de separar en microfrontends.
    Es importante pensar en esta arquitectura como una ayuda en nuestra manera de organizar los equipos y en sus objetivos individuales, teniendo en cuenta los despliegues independientes que nos ofrece, no tanto en separar únicamente la interfaz en distintos bloques dado que esto ya nos lo pueden ofrecer los componentes por sí solos.
  3. Pensar en la performance: los microfrontends indirectamente pueden ser nuestro mejor aliado para tratar de obtener un buen rendimiento en nuestra aplicación debido al code splitting o la distribución del código que hace cada microfrontend en varios bloques de código más pequeños.
    Gracias a ello, en función de cómo orientemos nuestra aplicación (todos los microfrontends en una misma homepage, un microfrontend por cada ruta de la app, un microfrontend por bloque de funcionalidad…) podremos cargarlos bajo demanda, evitando al navegador procesar bloques de código que aún no sean necesarios para el usuario en ese momento.
Bloques de código más pequeño
  1. Cuidado con la reusabilidad: el motivo de que decidamos implementar una aplicación con microfrontends no debe ser exclusivamente por buscar la reusabilidad de los elementos. Esto puede suponer un problema en el futuro, puesto que ante una incidencia o bug deberíamos tocar el código en varios microfrontends, provocando la coordinación con el despliegue de otro equipo y, por tanto, rompiendo con la independencia entre ellos.
    Todos conocemos la importancia de reutilizar bloques de código en el software para evitar duplicidades, pero en el caso de los microfrontends debemos evitar desarrollar funcionalidades pensando desde el principio en su posible reutilización dado que en la mayoría de los microfrontends difícilmente vamos a reutilizar su funcionalidad por ser tan específica a cada dominio del negocio. (p.e: listado de productos, carrito de la compra, etc)
  2. Evitar que nuestros microfrontends afecten a la estandarización y aspecto de nuestra página: es fundamental definir e implementar los distintos microfrontends a partir de un sistema de diseño, librería de componentes o simplemente una identidad propia bien definida para mantener la consistencia entre todos los elementos de la interfaz y evitar frankensteins_ que rompan con la estética de nuestra aplicación.
  3. Comunicación entre microfrontends: es importante definir cómo vamos a querer que nuestros microfrontends se comuniquen entre sí. ¿Vamos a necesitar hacer uso de un estado global almacenado de la aplicación o nos basta con la propagación de eventos mediante la propia API del navegador?
    Emplear librerías de terceros para este fin como por ejemplo Redux, Zustand, MobX o el propio Context API de React choca directamente con el principio de independencia de los microfrontends. La comunicación entre los microfrontends debería ser mínima, por el contrario, dos o más microfrontends que se comunican con mucha frecuencia quizá sea motivo de que deban ser una misma entidad y no varias independientes.
  4. Evitar el uso de microfrontends en proyectos pequeños o en los que la escalabilidad a futuro no sea una prioridad, como por ejemplo: un blog personal, un portafolio para mostrar nuestros proyectos, una prueba de concepto, etc.
    Si tu aplicación o equipo ha crecido notablemente en los últimos meses, como podría ser en el caso en una startup, siempre podréis valorar el paso a microfrontends separando poco a poco la interfaz en los distintos dominios del negocio.

Conclusiones

La arquitectura de microfrontends puede resultar muy buena opción en aplicaciones de gran tamaño o en las que hay muchos equipos de desarrollo involucrados. La implementación de este tipo de arquitectura trae consigo un conjunto de buenas prácticas implícitas para mejorar nuestra forma de trabajar, como es el desarrollo de forma modularizada e independiente o la gestión de los equipos de manera vertical, siempre con el foco en una buena escalabilidad para evitar elevados costes que puedan surgir al crecer como equipo y producto.

Son varias las empresas que actualmente han optado por esta arquitectura en algunos de sus productos, por lo que podemos afirmar que su aplicación tiene verdadera cabida en el mercado actual.

La prueba de concepto realizada para escribir este artículo se encuentra disponible en el siguiente repositorio.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.