Frontend de microservicios: un enfoque moderno para la separación del frente

spa demasiado gordo


La arquitectura de microservicios ha sido durante mucho tiempo el estándar de facto en el desarrollo de sistemas grandes y complejos. Tiene una serie de ventajas: es una división estricta en módulos, conectividad débil, resistencia a fallas, salida gradual a la producción y versiones independientes de componentes.

Es cierto que, a menudo, hablando de arquitectura de microservicios, solo se menciona la arquitectura de back-end, y la interfaz, como era, sigue siendo monolítica. Resulta que hicimos un gran respaldo, y el frente nos retrae.

Hoy les diré cómo hicimos el frente de microservicio en nuestra solución SaaS y qué problemas encontramos.

Problema


Inicialmente, el desarrollo en nuestra empresa se veía así: hay muchos equipos involucrados en el desarrollo de microservicios, cada uno de los cuales publica su propia API. Y hay un equipo separado que desarrolla SPA para el usuario final, utilizando la API de diferentes microservicios. Con este enfoque, todo funciona: los desarrolladores de microservicios saben todo acerca de su implementación, y los desarrolladores de SPA conocen todas las complejidades de las interacciones de los usuarios. Pero había un problema: ahora cada front-end debería conocer todas las complejidades de todos los microservicios. Cada vez hay más microservicios, más y más proveedores front-end, y Agile está comenzando a desmoronarse, a medida que aparece la especialización dentro del equipo, es decir, la intercambiabilidad y la universalidad desaparecen.

Entonces llegamos a la siguiente etapa: desarrollo modular. El equipo frontend se dividió en subcomandos. Cada uno era responsable de su parte de la aplicación. Se ha vuelto mucho mejor, pero con el tiempo, este enfoque se ha agotado por varias razones.

  • Todos los módulos son heterogéneos, con sus propios detalles. Para cada módulo, su propia tecnología es más adecuada. Al mismo tiempo, la elección de la tecnología es una tarea difícil en las condiciones de SPA.
  • Dado que la aplicación SPA (y en el mundo moderno, esto significa compilar en un solo paquete o al menos un conjunto), solo se puede emitir la aplicación completa al mismo tiempo. El riesgo de cada extradición está creciendo.
  • Cada vez es más difícil administrar la dependencia. Los diferentes módulos necesitan versiones de dependencia diferentes (posiblemente específicas). Alguien no está listo para cambiar a la API de dependencia actualizada, y alguien no puede hacer una función debido a un error en la rama de dependencia anterior.
  • Debido al segundo punto, el ciclo de liberación de todos los módulos debe estar sincronizado. Todos esperan a los rezagados.

Frontend de corte


Llegó un momento de acumulación de masa crítica, y decidieron dividir el front-end en ... microservicios front-end. Definamos qué es un microservicio front-end:

  • una parte completamente aislada de la IU, de ninguna manera dependiente de otros; aislamiento radical; literalmente desarrollado como una aplicación independiente;
  • cada microservicio de front-end es responsable de un determinado conjunto de funciones comerciales de principio a fin, es decir, es completamente funcional en sí mismo;
  • Se puede escribir en cualquier tecnología.

Pero fuimos más allá e introdujimos otro nivel de división.

Concepto de fragmento


Llamamos a un fragmento un paquete compuesto por js + css + . De hecho, esta es una parte independiente de la interfaz de usuario, que debe cumplir con un conjunto de reglas de desarrollo para que pueda usarse en un SPA general. Por ejemplo, todos los estilos deben ser lo más específicos posible para el fragmento. No se debe intentar interactuar directamente con otros fragmentos. Debe tener un método especial al que pueda pasar el elemento DOM donde se debe dibujar el fragmento.

Gracias al descriptor, podemos guardar información sobre todos los fragmentos registrados del entorno y luego tener acceso a ellos por ID.

Este enfoque le permite colocar dos aplicaciones escritas en diferentes marcos en una página. También permite escribir código universal que le permitirá cargar dinámicamente los fragmentos necesarios en la página, inicializarlos y administrar el ciclo de vida. Para la mayoría de los marcos modernos, es suficiente seguir las "reglas de higiene" para que esto sea posible.

En los casos en que el fragmento no tiene la oportunidad de "convivir" con otros en la misma página, hay un script de respaldo en el que dibujamos el fragmento en un iframe (la solución a los problemas relacionados está más allá del alcance de este artículo).

Todo lo que debe hacer un desarrollador que quiera usar un fragmento existente en una página es:

  1. Conecte el script de la plataforma de microservicios a la página.
     <script src="//{URL to static cache service}/api/v1/mui-platform/muiPlatform.js"></script> 

  2. Llame al método de agregar un fragmento a la página.

     window.MUI.createFragment( // fragment name "hello-label", // fragment model { text: "HelloLabelFragment text from run time" }, // fragment position { selector: ".hello-label-placeholder", position: "afterend" }) .then(callback); 


Además, para la comunicación entre fragmentos, hay un bus construido en Observable y rxjs . Está escrito en NativeJS. Además, el SDK viene con envoltorios para varios marcos que ayudan a usar este bus de forma nativa. Un ejemplo para Angular 6 es un método de utilidad que devuelve rxjs/Observable :

 import {fromEvent} from "@netcracker/mui-platform/angular2-factory/modules/shared/utils/event-utils" fromEvent("<event-name>"); fromEvent(EventClassType); 

Además, la plataforma proporciona un conjunto de servicios que a menudo utilizan diferentes fragmentos y son básicos en nuestra infraestructura. Estos son servicios como localización / internacionalización, servicio de autorización, trabajo con cookies entre dominios, almacenamiento local y mucho más. Para su uso, el SDK también proporciona envoltorios para varios marcos.

Combinando la interfaz


Por ejemplo, podemos considerar este enfoque en el área de administración de SPA (combina diferentes configuraciones posibles de diferentes microservicios). Podemos hacer que el contenido de cada marcador sea un fragmento separado, que será entregado y desarrollado por separado por cada microservicio. Gracias a esto, podemos hacer un simple "encabezado" que mostrará el microservicio correspondiente al hacer clic en un marcador.

imagen

Desarrollamos la idea de un fragmento.


El desarrollo de un marcador con un fragmento no siempre nos permite resolver todos los problemas posibles. A menudo es necesario desarrollar una cierta parte de la interfaz de usuario en un microservicio, que luego se reutilizará en otro microservicio.

¡Y aquí los fragmentos también nos ayudan! Como todo lo que necesita el fragmento es un elemento DOM para la representación, le damos a cualquier microservicio una API global a través de la cual puede colocar cualquier fragmento dentro de su árbol DOM. Para hacer esto, simplemente pase la ID del fragmento y el contenedor en el que debe dibujarse. ¡El resto se hará solo!
Ahora podemos construir una "muñeca de anidación" de cualquier nivel de anidación y reutilizar piezas enteras de IU sin la necesidad de soporte en varios lugares.

A menudo sucede que en una página hay varios fragmentos que deberían cambiar su estado al cambiar algunos datos comunes en la página. Para hacer esto, tienen un bus de eventos global (NativeJS) a través del cual pueden comunicarse y responder a los cambios.

imagen

Servicios compartidos


En una arquitectura de microservicio, los servicios centrales aparecen inevitablemente, datos que todos los demás necesitan. Por ejemplo, un servicio de localización que almacena traducciones. Si cada microservicio comienza individualmente a subir estos datos al servidor, solo recibimos un montón de solicitudes durante la inicialización.

Para resolver este problema, hemos desarrollado implementaciones de servicios NativeJS que proporcionan acceso a dichos datos. Esto hizo posible no realizar solicitudes innecesarias y datos de caché. En algunos casos, incluso envíe dichos datos a una página en HTML por adelantado para deshacerse por completo de las solicitudes.

Además, se desarrollaron envoltorios sobre nuestros servicios para diferentes marcos para hacer que su uso sea muy natural (DI, interfaz fija).

Pros de microservicios front-end


Lo más importante que obtenemos al dividir un monolito en fragmentos es la capacidad de seleccionar tecnologías para cada equipo individualmente y la gestión transparente de la dependencia. Pero también, da lo siguiente:

  • áreas de responsabilidad muy claramente divididas;
  • emisión independiente: cada fragmento puede tener su propio ciclo de liberación;
  • aumentar la estabilidad de la solución en su conjunto, ya que la emisión de fragmentos individuales no afecta a otros;
  • la capacidad de revertir fácilmente las funciones, desplegarlas parcialmente a una audiencia;
  • el fragmento se coloca fácilmente en la cabeza de cada desarrollador, lo que lleva a
intercambiabilidad de los miembros del equipo; Además, cada front-end puede comprender mejor todas las complejidades de interactuar con el back-end correspondiente.

Una solución con un microseris front-end se ve bien. De hecho, ahora cada fragmento (microservicio) puede decidir por sí mismo cómo implementarlo: si solo necesita nginx para distribuir estadísticas, un middleware completo para agregar solicitudes de respaldo o soporte websockets, o algunos otros detalles en forma de un protocolo de transferencia de datos binarios dentro de http. Además, los fragmentos pueden elegir sus propios métodos de ensamblaje, métodos de optimización y más.

Contras de microservicios front-end


Nunca puedes prescindir de una mosca en la pomada.

  • La interacción entre fragmentos no puede garantizarse mediante métodos de tubo estándar (DI, por ejemplo).
  • ¿Qué hacer con las dependencias compartidas? Después de todo, el tamaño de la aplicación crecerá a pasos agigantados, si no se extraen de los fragmentos.
  • De todos modos, solo uno debe ser responsable del enrutamiento en la aplicación final.
  • Qué hacer si uno de los fragmentos es inaccesible / no se puede dibujar.
  • No está claro qué hacer con el hecho de que diferentes microservicios pueden estar en diferentes dominios.

Conclusión


Nuestra experiencia con este enfoque ha demostrado su viabilidad. La velocidad de salida de las características en producción ha aumentado significativamente. El número de dependencias implícitas entre partes de la interfaz se redujo a casi cero. Tenemos una interfaz de usuario consistente. Puede probar características de forma segura sin involucrar a un gran número de personas.

Desafortunadamente, en un artículo es muy difícil cubrir toda la gama de problemas y soluciones que se pueden encontrar en la forma de repetir dicha arquitectura. Pero para nosotros, los pros claramente superan a los contras. Si Habr muestra interés en revelar detalles de la implementación de este enfoque, ¡escribiremos una secuela!

Source: https://habr.com/ru/post/es420753/


All Articles