La arquitectura de micro frontends es extremadamente poderosa cuando se trata de dividir grandes monolitos frontends en bloques más pequeños y desplegables de forma individual, cada uno propiedad de un equipo autónomo y enfocado en un dominio empresarial. Pero, ¿qué pasa con el Estado? A menudo se nos dice que los micro frontends no deben compartir estado, ya que esto los acoplaría entre sí. Sin embargo, cuando se trata de interfaces de usuario complejas, no es raro encontrarse con escenarios donde es necesario gestionar el estado entre micro frontends. Esta charla trata sobre encontrar el punto óptimo: ¿En qué escenarios es razonable que los micro frontends compartan estado? ¿Y cómo deberían compartir estado los micro frontends manteniéndose desacoplados entre sí? Discutimos y comparamos diferentes soluciones en React.
Compartir es Cuidar: ¿Deberían los Micro Frontends Compartir Estado?
Video Summary and Transcription
Los micro frontends permiten que los equipos de desarrollo trabajen de forma autónoma y con menos fricciones y limitaciones. Organizar los equipos en torno a preocupaciones empresariales, en línea con los subdominios, en lugar de preocupaciones técnicas, puede llevar a un software bien dividido y a historias de usuario bajo la responsabilidad de un solo equipo. Tener un respaldo o punto de referencia lógico es importante para implementar micro frontends sin romper su aislamiento. Los micro frontends pueden comunicarse entre sí utilizando el objeto window y eventos personalizados. Los micro frontends deben mantenerse aislados mientras mantienen la comunicación a través de diversos enfoques.
1. Introducción a los Micro-frontends
Los Micro-frontends permiten que los equipos de desarrollo trabajen de manera autónoma y con menos fricciones y limitaciones. Exploraremos una perspectiva orientada al dominio para comprender la comunicación entre los micro-frontends. Resolver las historias de usuario debería ser una tarea de extremo a extremo de un solo equipo, en lugar de ser compartida entre varios equipos. Las historias de usuario se organizan según los subdominios.
Hola a todos. Mi nombre es Eliran Atan. Esta es mi segunda vez en React Summit, pero aún estoy muy emocionado. He estado construyendo sistemas escalables durante la última década, y hoy quiero centrarme en los micro-frontends, y especialmente en el intercambio de estado de los micro-frontends.
Entonces, los micro-frontends se tratan de dividir este monolito de frontend en piezas más pequeñas y manejables que permiten que los equipos de desarrollo trabajen de manera más eficiente y efectiva. Esto se debe a que los micro-frontends suelen estar aislados entre sí. Permiten que los equipos de desarrollo trabajen de manera autónoma y con menos fricciones y limitaciones.
Un problema común que surge cuando hablamos de micro-frontends es si deben compartir estado o comunicarse de alguna manera. Si es así, ¿cómo deberíamos hacerlo sin romper su aislamiento entre sí? En esta charla veremos un enfoque diferente para pensar en los micro-frontends. Vamos a abordar eso desde una perspectiva orientada al dominio. Veremos cómo podemos representar un micro-frontend utilizando algo que llamamos un contexto acotado. El contexto acotado es esta entidad lógica que refleja nuestra implementación y puede derivar la implementación. Y veremos cómo eso puede ayudarnos a comprender la comunicación entre los micro-frontends.
Entonces, empecemos desde otro ángulo. Hablemos de Agile. En Agile, a menudo trabajamos con historias de usuario. Las historias de usuario son esta breve descripción de un objetivo final que el usuario desea lograr, ¿verdad? Y siempre se describe desde la perspectiva del usuario y en términos de el negocio. Por lo general, en muchas organizaciones, resolver una historia de usuario implicará la cooperación y coordinación de equipos de múltiples capas. Y eso se debe a que el código afectado para resolver esa historia a menudo se comparte entre estos equipos. Y eso se debe a cómo las organizaciones tienden a estructurar los equipos, especialmente cuando hablamos de front-end. A menudo vemos esta división arbitraria de responsabilidades entre equipos. A veces se trata de ser dueño de componentes o páginas o vistas. Pero eso no es realmente cómo se organizan las historias de usuario. Lo que realmente queremos es alguna forma de organizar nuestros equipos y código de manera que resolver una historia de usuario sea una tarea de extremo a extremo de un solo equipo. Eso significa que el código afectado para resolver esa historia pertenecería a un solo equipo determinado. Eso hará que el flujo sea mucho mejor, especialmente a gran escala. Una pregunta que debemos hacernos es cómo se organizan las historias de usuario. Desde una perspectiva orientada al dominio, las historias de usuario se organizan según los subdominios. Si tomamos una aplicación de e-commerce estándar, ese es el dominio más simple que podemos pensar, entonces una división normal en subdominios sería algo como catálogo, pedido, entrega, soporte y tal vez algunos otros subdominios. El subdominio del catálogo se ocupa de permitir a los usuarios navegar y buscar productos, mientras que los otros subdominios se ocupan de que los usuarios agreguen productos a
2. Implementación y Comunicación de Microfrontends
Organizar equipos en torno a preocupaciones comerciales, alineados con subdominios en lugar de preocupaciones técnicas, puede llevar a un software dividido de manera ordenada y a historias de usuario bajo la responsabilidad de un solo equipo. Los microfrontends mejoran la segregación entre subdominios y brindan autonomía a los equipos sobre la pila tecnológica. El diseño impulsado por el dominio, específicamente el DDD estratégico, ayuda a descomponer el dominio en subdominios. Cada contexto acotado deriva una implementación de un microfrontend, simplificando la implementación y mejorando la comunicación. La herramienta de mapeo de contexto ayuda a comprender las relaciones entre diferentes contextos y permite una comunicación efectiva entre los microfrontends.
Ahora, esto se puede llevar más lejos, y aquí es donde hablamos nuevamente de los microfrontends. Al dividir este monolito en diferentes microfrontends, mejoraremos la segregación entre subdominios y daremos a los equipos otro nivel de autonomía en el nivel de la pila tecnológica, ya que ahora pueden elegir sus propias herramientas y patrones de diseño, por ejemplo. Cada equipo puede optimizar los patrones de diseño que utiliza para los objetivos específicos que desea lograr.
Entonces, mi punto aquí es que una base sólida para los microfrontends es considerar la alineación con los aspectos comerciales. Es por eso que cuando hablamos de microfrontends, debemos pensar en el diseño impulsado por el dominio, específicamente en el DDD estratégico. El DDD estratégico es esta herramienta que nos permite descomponer correctamente nuestro dominio en varios subdominios de una manera que maximice el potencial que tiene el microfrontend. El DDD realmente nos dice que debemos reunir a nuestros expertos en el dominio para tomar el tiempo y hacer una lluvia de ideas junto con otras partes interesadas para formar este lenguaje inequívoco que realmente capture las entidades y procesos naturales que ocurren dentro de este subdominio. Y esta forma de lenguaje forma un límite lógico que llamamos contexto acotado. El contexto acotado tiene mucho que ofrecer, muchos beneficios para trabajar con el contexto acotado y derivar de él la implementación real del microfrontend. Entonces, cada contexto acotado derivará una implementación de un cierto microfrontend. Podríamos resumir eso diciendo que un microfrontend es una implementación técnica de un contexto acotado. Por lo tanto, tener un contexto acotado que deriva microfrontends tiene numerosos beneficios. El contexto acotado puede simplificar drásticamente la implementación de cada microfrontend porque cada término en cada contexto acotado se describe desde la perspectiva de ese contexto acotado. Por lo tanto, no tienes mucha información redundante que debas manejar. Solo manejas las cosas relevantes dentro de ese contexto. Y hay un beneficio más profundo en eso. Y ahora tenemos esto en cada contexto, tenemos un lenguaje cohesivo inequívoco que todos entienden. Y eso mejora mucho la comunicación. Y eso hace que traducir las historias de usuario sea mucho más fácil, y así sucesivamente.
Pero quiero centrarme en otro beneficio que se relaciona con cómo debe comunicarse un microfrontend. Tenemos otra herramienta que forma parte del kit de herramientas del DDD, que es la herramienta de mapeo de contexto. El mapeo de contexto se trata de comprender, hacer una lluvia de ideas y comprender las relaciones entre diferentes contextos. Por ejemplo, aunque el término `producto` generalmente significa otra cosa cuando hablamos del contexto del catálogo, por ejemplo, una estructura que muestra todos los comentarios, reseñas, detalles del producto, imágenes del producto, y así sucesivamente, eso significa que `producto` significará otra cosa en el contexto del pedido. Probablemente tendrá algún identificador, precio, descuentos o todo lo que se relacione con el pedido. Pero aún así, aunque estén definidos de manera diferente en esos diferentes contextos, aún tienen esta conexión lógica entre ellos, ¿verdad? Porque queremos permitir a los usuarios agregar productos que encuentren en el catálogo a algún carrito que es naturalmente parte del contexto del pedido. Y eso realmente genera una comunicación que debemos tener entre el frontend del catálogo y el frontend del pedido, ¿verdad? Esos fragmentos de información, un producto seleccionado, deben comunicarse entre estos microfrontends. Entonces, aquí vemos una forma de comprender lógicamente cuáles son las conexiones que debemos hacer entre los contextos, y luego derivar a partir de ahí la comunicación entre la implementación, entre los microfrontends reales. Y eso puede ayudarnos a evitar comunicaciones innecesarias, y también puede ayudarnos a comprender si nuestro modelo es correcto, si no estamos creando demasiado
3. Implementación de la Comunicación entre Microfrontends
Tener una copia de seguridad lógica o un punto de referencia es importante para implementar microfrontends sin romper su aislamiento. Utilizar el backend para comunicar productos seleccionados manteniendo el aislamiento es un enfoque directo. Sin embargo, esto puede llevar a desventajas en la experiencia del usuario y a una complejidad del frontend trasladada al backend, lo que causa cuellos de botella en el desarrollo.
4. Comunicación Directa Entre Microfrontends
Los microfrontends pueden comunicarse entre sí utilizando el objeto window y eventos personalizados. Para gestionar estados complejos, se puede utilizar un almacén centralizado de Redux, con cada microfrontend teniendo su propio almacén. Otra opción es permitir que los microfrontends se suscriban o vean los almacenes de otros microfrontends sin la capacidad de modificarlos. Para garantizar la seguridad y la alineación, se puede implementar un almacén global, con cada microfrontend implementando una API específica. En resumen, los microfrontends deben mantenerse aislados mientras mantienen la comunicación a través de diversos enfoques.
Entonces, hablemos sobre la comunicación directa entre microfrontends. Este es un problema muy común en la ingeniería de software. Tenemos estas unidades diferentes que son independientes entre sí. No queremos que estén acopladas entre sí, pero aún así tienen que intercambiar mensajes entre sí porque son parte de un equipo más grande.
Si observamos lo que estamos haciendo en el diseño de software de backend, generalmente se trata de microservicios y microfrontend. Los microservicios y los microfrontend tienen cierta correlación entre ellos. Esa es una oportunidad para aprender mucho sobre los microfrontends. Entonces, si observamos cómo se comunican los microservicios, uno de los mecanismos más populares y poderosos es el mecanismo de suscripción pública, la mensajería asíncrona. Cada microservicio puede publicar información interesante que sucede dentro del microservicio en un espacio compartido, como un bus de mensajes, y cualquier otro microservicio puede consumir esta información en caso de que sea realmente interesante. Esto hace que los microservicios sigan estando desacoplados entre sí de alguna manera, pero aún permite una comunicación muy agradable y poderosa. ¿Podríamos derivar de eso una solución para nuestros microfrontends, verdad? Podríamos usar el objeto window, que es un gran espacio común con el que cada microfrontend puede comunicarse. Y cada microfrontend podría publicar eventos interesantes sobre cosas que suceden, como un producto que se seleccionó para la tarjeta, y cualquier otro microfrontend podría consumir esta información. Entonces, los diferentes microfrontends no son conscientes entre sí, pero aún pueden pasar información importada. Esto se puede implementar de manera muy fácil e incluso nativamente utilizando eventos personalizados. Podemos crear eventos personalizados y enviarlos al objeto window, y podemos agregar escuchadores de eventos al objeto window para obtener esta información de vuelta. Por lo tanto, podrías envolver ese mecanismo con tu framework de React o Vue y construir algo más avanzado que pueda ayudarte a automatizar parte de esta comunicación o hacerla más robusta.
Ahora, eso fue bueno, pero a veces, simplemente tienes que tener un estado. Entonces, comunicarse con el evento es genial, pero hay algunas operaciones complejas o algunas construcciones de datos complejas que queremos tener compartidas o algún tipo de estados como un estado de Redux para gestionar el proceso, y eso puede suceder con bastante frecuencia. Entonces, lo que querríamos es un almacén centralizado de Redux y cada microfrontend podría comunicarse con él para mantener este tipo de estados complejos. Y esto es muy problemático, especialmente cuando hablamos de microfrontends, porque rompe por completo el aislamiento que tienen entre sí, especialmente porque son propiedad de diferentes equipos. Entonces, si el microfrontend de producto manipulara el estado de alguna manera, entonces el microfrontend de entrega y el microfrontend de soporte tendrían que adaptarse o comprender el cambio y eso puede volverse muy difícil. Entonces, lo que realmente queremos son estos almacenes alineados. Tenemos un almacén por microfrontend. Cada microfrontend puede usar ese almacén como un almacén de Redux, y estos almacenes están desacoplados entre sí. Pero, por supuesto, aquí no hay ninguna comunicación entre ellos y eso también es problemático. Otra solución que he visto para mantener un estado entre microfrontends de una manera que aún los mantiene bastante desacoplados es permitir que los microfrontends se suscriban o vean los almacenes de otros microfrontends, pero sin la capacidad de modificar estos almacenes. Entonces, en este ejemplo, el microfrontend de soporte realmente puede ver el almacén del microfrontend de producto y el microfrontend de entrega, pero aún hay algunos problemas aquí, porque cualquier cambio en cómo se implementa el almacén de soporte afectaría a los microfrontends de entrega y producto. Entonces, lo que podemos hacer para que sea aún más seguro es idear este almacén global. Este almacén global gestiona los diferentes almacenes de los diferentes microfrontends, cada microfrontend puede suscribirse al almacén, tiene su propio almacén que puede manipular, y otros microfrontends pueden consumir y suscribirse a sus propios almacenes u otros almacenes. Debido a que este almacén global expondría una API específica, cada microfrontend solo tendría que implementar esa API específica y eso facilitaría todo mucho más, alineando esta API en esos diferentes microfrontends.
Bueno, eso fue una descripción general de tres formas comunes de mantener la comunicación entre microfrontends mientras los mantenemos bastante aislados entre sí. Me gustaría resumir la charla y lo primero que decimos es que debemos esforzarnos por tener equipos altamente autónomos, debemos estructurar los equipos en alineación con los subdominios comerciales y eso no se relaciona directamente con los microfrontends, definitivamente se puede tener un monolito que esté alineado con un aspecto comercial. Aunque aplicar la arquitectura de microfrontends podría mejorar esta idea al segregar mejor los aspectos comerciales. Y al aplicar DTD estratégico, podemos identificar subdominios, modelarlos en contextos acotados y aplicar el mapeo de contextos para encontrar relaciones entre ellos. Y también decimos que el microfrontend es una implementación técnica del contexto acotado. El mapeo de contextos puede ayudarnos a identificar los puntos donde los microfrontends deben comunicarse. Y una regla importante es que la comunicación entre microfrontends debe mantenerlos aislados entre sí. Ese es el resumen de la charla. Espero haberles dado algo de inspiración sobre cómo aplicar DDD en los problemas que encontramos en el frontend y cómo facilitar la comunicación entre microfrontends de una manera que les ayude a alcanzar objetivos como la autonomía de los equipos. Muchas gracias.