Patrón de higo estrangulador - Recomendaciones de AWS

Patrón de higo estrangulador

Intención

El patrón de higo estrangulador ayuda a migrar una aplicación monolítica a una arquitectura de microservicios de manera gradual, con un riesgo menor de transformación y una disrupción menor empresarial.

Motivación

Las aplicaciones monolíticas se desarrollan para proporcionar la mayor parte de sus funciones en un solo proceso o contenedor. El código está acoplado de manera ajustada. Por este motivo, para hacer cambios en las aplicaciones son necesarias nuevas pruebas exhaustivas para evitar problemas de regresión. Los cambios no se pueden probar de manera aislada, lo que afecta a la duración del ciclo. A medida que la aplicación se enriquece con más características, la alta complejidad puede implicar que se dedique más tiempo al mantenimiento, un aumento del tiempo de comercialización y, en consecuencia, una ralentización de la innovación de los productos.

Cuando el tamaño de la aplicación se escala, aumenta la carga cognitiva del equipo y puede provocar que los límites de responsabilidad del equipo no estén claros. No es posible escalar las características individuales según la carga. Es necesario escalar toda la aplicación para permitir la carga máxima. A medida que los sistemas envejecen, la tecnología puede quedar obsoleta, lo que aumenta los costos de soporte. Las aplicaciones antiguas y monolíticas siguen las prácticas recomendadas que estaban disponibles en el momento del desarrollo y no se diseñaron para distribuirse.

Cuando se migra una aplicación monolítica a una arquitectura de microservicios, se puede dividir en componentes más pequeños. Estos componentes se pueden escalar de manera independiente, se pueden lanzar de manera independiente y pueden ser responsabilidad de equipos individuales. Esto da lugar a una mayor velocidad de cambio, ya que los cambios se localizan y se pueden probar y publicar de manera rápida. Los cambios tienen un ámbito de impacto menor porque los componentes están acoplados de manera débil y se pueden implementar de manera individual.

Sustituir por completo un monolito por una aplicación de microservicios mediante la reescritura o la refactorización del código es una tarea enorme y un riesgo grande. Una migración a gran escala, en la que el monolito se migra en una sola operación, supone un riesgo de transformación y una disrupción empresarial. Mientras se refactoriza la aplicación, es extremadamente difícil o incluso imposible agregar características nuevas.

Una manera de resolver este problema es utilizar el patrón de higo estrangulador, que introdujo Martin Fowler. Este patrón implica pasar a los microservicios mediante la extracción gradual de características y la creación de una aplicación nueva en torno al sistema existente. Las características del monolito se sustituyen de manera gradual por microservicios. Los usuarios de las aplicaciones pueden utilizar las características recién migradas de manera progresiva. Cuando se trasladen todas las características al sistema nuevo, la aplicación monolítica se podrá retirar de manera segura.

Aplicabilidad

Utilice el patrón de higo estrangulador cuando suceda lo siguiente:

  • Desea migrar la aplicación monolítica de manera gradual a una arquitectura de microservicios.

  • Un enfoque de migración radical es riesgoso debido al tamaño y la complejidad del monolito.

  • La empresa quiere agregar características nuevas y no puede esperar a que se complete la transformación.

  • Los usuarios finales deben verse afectados lo menos posible durante la transformación.

Problemas y consideraciones

  • Acceso a la base del código: para implementar el patrón de higo estrangulador, debe tener acceso a la base del código de la aplicación monolítica. A medida que se vayan migrando las características del monolito, será necesario hacer cambios pequeños en el código e implementar una capa anticorrupción en el monolito para enrutar las llamadas a microservicios nuevos. No puede interceptar llamadas sin acceso a la base del código. El acceso a la base del código también es fundamental para redirigir las solicitudes entrantes. Es posible que sea necesario refactorizar el código para que la capa del proxy pueda interceptar las llamadas de las características migradas y enrutarlas a los microservicios.

  • Dominio poco claro: la descomposición prematura de los sistemas puede resultar costosa, sobre todo cuando el dominio no está claro y es posible definir mal los límites de los servicios. El diseño basado en dominios (DDD) es un mecanismo para comprender el dominio. La tormenta de eventos es una técnica para determinar los límites del dominio.

  • Identificación de microservicios: puede utilizar el DDD como una herramienta clave para identificar los microservicios. Para identificar los microservicios, busque las divisiones naturales entre las clases de servicios. Numerosos servicios tendrán su propio objeto de acceso a los datos y se desacoplarán con facilidad. Los servicios que tienen una lógica empresarial relacionada y las clases que no tienen dependencias o que tienen pocas dependencias son buenos candidatos para convertirse en microservicios. Puede refactorizar el código antes de descomponer el monolito para evitar un acoplamiento ajustado. También debe tener en cuenta los requisitos de cumplimiento, la frecuencia de publicación, la ubicación geográfica de los equipos, las necesidades de escala, las necesidades tecnológicas basadas en casos de uso y la carga cognitiva de los equipos.

  • Capa anticorrupción: durante el proceso de migración, cuando las características del monolito tengan que llamar a las características que se migraron como microservicios, se debe implementar una capa anticorrupción (ACL) que dirija cada llamada al microservicio correspondiente. Con el fin de desacoplar a los autores de las llamadas existentes en el monolito y evitar que se produzcan cambios en estos, la ACL funciona como un adaptador o una fachada que convierte las llamadas en una interfaz más nueva. Esto se analiza en detalle en una sección anterior sobre la implementación del patrón de ACL que figura en esta guía.

  • Error en la capa del proxy: durante la migración, una capa proxy intercepta las solicitudes que van a la aplicación monolítica y las dirige al sistema heredado o al sistema nuevo. Sin embargo, esta capa del proxy puede convertirse en un solo punto de error o en un obstáculo para el rendimiento.

  • Complejidad de la aplicación: los monolitos grandes son los que más se benefician del diseño de higo estrangulador. En el caso de las aplicaciones pequeñas, en las que la complejidad de una refactorización completa es baja, podría resultar más eficiente volver a escribir la aplicación en una arquitectura de microservicios en lugar de migrarla.

  • Interacciones de servicios: los microservicios se pueden comunicar de manera sincrónica o asíncrona. Cuando se es necesaria una comunicación sincrónica, considere si los tiempos de espera pueden provocar el consumo de conexiones o del grupo de subprocesos y provocar que las aplicaciones tengan problemas de rendimiento. En esos casos, utilice el patrón de disyuntores para devolver el error inmediato en el caso de las operaciones en las que se puedan producir errores durante periodos prolongados. La comunicación asíncrona se puede lograr mediante el uso de eventos y colas de mensajes.

  • Agregación de datos: en una arquitectura de microservicios, los datos se distribuyen en las bases de datos. Cuando sea necesario agregar datos, puede utilizar AWS AppSync en el frontend o el patrón de división de responsabilidades de consulta de comandos (CQRS) en el backend.

  • Coherencia de datos: los microservicios tienen su propio almacén de datos y la aplicación monolítica también puede utilizar estos datos. Para permitir el uso compartido, puede sincronizar el almacén de datos de los microservicios nuevos con la base de datos de la aplicación monolítica mediante una cola y un agente. Sin embargo, esto puede provocar redundancia de datos y una coherencia final entre dos almacenes de datos, por lo que le recomendamos que lo trate como una solución táctica hasta que pueda establecer una solución a largo plazo, como un lago de datos.

Implementación

Según el patrón del higo estrangulador, se sustituye una funcionalidad específica por un servicio o una aplicación nuevos, un componente a la vez. Una capa del proxy intercepta las solicitudes que van a la aplicación monolítica y las dirige al sistema heredado o al sistema nuevo. Como la capa del proxy dirige a los usuarios a la aplicación correcta, puede agregar características al sistema nuevo y, al mismo tiempo, garantizar que siga funcionando el monolito. Con el tiempo, el sistema nuevo sustituirá todas las características del sistema anterior y podrá retirarlo del servicio.

Arquitectura de alto nivel

En el diagrama siguiente, una aplicación monolítica tiene tres servicios: servicio de usuario, servicio de carrito y servicio de cuentas. El servicio de carrito depende del servicio de usuario y la aplicación utiliza una base de datos relacional monolítica.

Aplicación monolítica con tres servicios.

El primer paso es agregar una capa del proxy entre la interfaz de usuario de la tienda y la aplicación monolítica. Al principio, el proxy enruta todo el tráfico a la aplicación monolítica.

Introducción de un proxy a la aplicación monolítica.

Cuando quiera agregar características nuevas a la aplicación, debe implementarlas como microservicios nuevos en lugar de agregar características al monolito existente. Sin embargo, sigue corrigiendo errores en el monolito para garantizar la estabilidad de la aplicación. En el diagrama siguiente, la capa del proxy enruta las llamadas al monolito o al microservicio nuevo según la URL de la API.

El proxy enruta las llamadas al monolito o a un microservicio nuevo.

Introducción de una capa anticorrupción

En la arquitectura siguiente, el servicio de usuario se migró a un microservicio. El servicio de carrito llama al servicio de usuario, pero la implementación deja de estar disponible en el monolito. Además, es posible que la interfaz del servicio recién migrado no coincida con la interfaz anterior de la aplicación monolítica. Para abordar estos cambios, debe implementar una ACL. Durante el proceso de migración, cuando las características del monolito tienen que llamar a las características que se migraron como microservicios, la ACL convierte las llamadas a la interfaz nueva y las enruta al microservicio correspondiente.

Introducción de una ACL para convertir las llamadas a la interfaz nueva.

Puede implementar la ACL dentro de la aplicación monolítica como una clase específica del servicio que se migró. Por ejemplo, UserServiceFacade o UserServiceAdapter. La ACL debe retirarse una vez que todos los servicios dependientes se hayan migrado a la arquitectura de microservicios.

Cuando se utiliza la ACL, el servicio de carrito sigue llamando al servicio de usuario en el monolito y el servicio de usuario redirige la llamada al microservicio a través de la ACL. El servicio de carrito debe seguir llamando al servicio de usuario sin saber de la migración del microservicio. Este acoplamiento débil es necesario para reducir la regresión y la disrupción empresarial.

Gestión de la sincronización de los datos

Se recomienda que el microservicio debe ser responsable de sus datos. El servicio de usuario almacena sus datos en su propio almacén de datos. Es posible que tenga que sincronizar los datos con la base de datos monolítica para gestionar las dependencias, como la generación de informes, y para que las aplicaciones posteriores que aún no están preparadas puedan acceder de manera directa a los microservicios. Es posible que la aplicación monolítica también requiera los datos para otras funciones y componentes que aún no se migraron a los microservicios. Por lo tanto, es necesaria la sincronización de datos entre el microservicio nuevo y el monolito. Para sincronizar los datos, puede introducir un agente de sincronización entre el microservicio del usuario y la base de datos monolítica, como se muestra en el diagrama siguiente. El microservicio de usuario envía un evento a la cola cada vez que se actualiza su base de datos. El agente de sincronización escucha la cola y actualiza de manera continua la base de datos monolítica. Por último, los datos de la base de datos monolítica son coherentes con los datos que se sincronizan.

Introducción de un agente de sincronización.

Migración de servicios adicionales

Cuando el servicio de carrito se migra de la aplicación monolítica, su código se revisa para llamar de manera directa al servicio nuevo, de modo que la ACL ya no enrute esas llamadas. En el siguiente diagrama se ilustra esta arquitectura.

Migración de servicios adicionales.

En el diagrama siguiente se muestra el estado final de estrangulamiento, en el que todos los servicios se migraron del monolito y solo queda el esqueleto del monolito. Los datos históricos se pueden migrar a los almacenes de datos que son responsabilidad de los servicios individuales. La ACL se puede eliminar y el monolito está listo para su retirada en esta etapa.

Estado final de estrangulamiento tras la migración de todos los servicios.

En el diagrama siguiente se muestra la arquitectura final tras la retirada de la aplicación monolítica. Puede alojar los microservicios individuales a través de una URL basada en recursos (por ejemplo http://www.storefront.com/user) o a través de su propio dominio (por ejemplo http://user.storefront.com), según los requisitos de la aplicación. Para más información sobre los métodos principales para exponer las API HTTP a los consumidores principales mediante nombres de host y rutas, consulte la sección Patrones de enrutamiento de API.

Arquitectura final tras la retirada del monolito.

Implementación mediante los servicios de AWS

Uso de API Gateway como proxy de la aplicación

En el diagrama siguiente se ilustra el estado inicial de la aplicación monolítica. Supongamos que se migró a AWS con una estrategia de lift-and-shift, por lo que se ejecuta en una instancia de Amazon Elastic Compute Cloud (Amazon EC2) y usa una base de datos de Amazon Relational Database Service (Amazon RDS). Para simplificar, la arquitectura utiliza una nube privada virtual (VPC) única con una subred privada y otra pública, y supongamos que los microservicios se implementarán desde un inicio en la misma Cuenta de AWS. (La práctica recomendada en los entornos de producción es utilizar una arquitectura de varias cuentas para garantizar la independencia de la implementación). La instancia de EC2 reside en una zona de disponibilidad única de la subred pública y la instancia de RDS reside en una zona de disponibilidad única de la subred privada. Amazon Simple Storage Service (Amazon S3) almacena activos estáticos como los archivos de JavaScript, CSS y React para el sitio web.

Estado inicial de la aplicación monolítica cuando se utiliza el patrón de higo estrangulador.

En la arquitectura siguiente, AWS Migration Hub Refactor Spaces implementa Amazon API Gateway delante de la aplicación monolítica. Refactor Spaces crea una infraestructura de refactorización dentro de la cuenta y API Gateway actúa como capa del proxy para enrutar las llamadas al monolito. Desde un inicio, todas las llamadas se enrutan a la aplicación monolítica a través de la capa del proxy. Como se mencionó anteriormente, las capas del proxy pueden convertirse en un punto de error único. Sin embargo, el uso de API Gateway como proxy mitiga el riesgo, ya que se trata de un servicio multi-AZ sin servidor.

Implementación del patrón de higo estrangulador con API Gateway.

El servicio de usuario se migra a una función de Lambda y una base de datos de Amazon DynamoDB almacena sus datos. Se agregan un punto de conexión del servicio de Lambda y una ruta predeterminada a Refactor Spaces. API Gateway se configura de manera automática para enrutar las llamadas a la función de Lambda.

Implementación del patrón de higo estrangulador con API Gateway: configuración del enrutamiento.

En el diagrama siguiente, el servicio de carrito también se migró del monolito a una función de Lambda. Se agregan una ruta y un punto de conexión de servicio adicionales a Refactor Spaces. El tráfico pasa de manera automática a la función de Lambda Cart. Amazon ElastiCache administra el almacén de datos de la función de Lambda. La aplicación monolítica aún permanece en la instancia de EC2 junto con la base de datos de Amazon RDS.

Traslado de un servicio del monolito con el patrón de higo estrangulador.

En el diagrama siguiente, el último servicio (cuenta) se migra del monolito a una función de Lambda. Sigue utilizando la base de datos de Amazon RDS original. Ahora, la arquitectura nueva tiene tres microservicios con bases de datos independientes. Cada servicio utiliza un tipo de base de datos distinto. Este concepto de uso de bases de datos personalizadas para satisfacer las necesidades específicas de los microservicios se denomina persistencia políglota. Las funciones de Lambda también se pueden implementar en distintos lenguajes de programación, según lo determine el caso de uso. Durante la refactorización, Refactor Spaces automatiza la transición y el enrutamiento del tráfico a Lambda. Esto ahorra a los desarrolladores el tiempo necesario para diseñar, implementar y configurar la infraestructura de enrutamiento.

Trasladar todos los servicios del monolito con el patrón del higo estrangulador.

Uso de varias cuentas

En la implementación anterior, utilizamos una VPC única con una subred privada y una pública para la aplicación monolítica, e implementamos los microservicios en la misma Cuenta de AWS por motivos de simplicidad. Sin embargo, esto es poco frecuente en situaciones reales, en las que los microservicios se suelen implementar en varias Cuentas de AWS para lograr una implementación independiente. En una estructura de varias cuentas, es necesario configurar el tráfico de enrutamiento desde el monolito a los servicios nuevos en cuentas distintas.

Refactor Spaces ayuda a crear y configurar la infraestructura de AWS para que las llamadas de la API se desvíen de la aplicación monolítica. Refactor Spaces orquesta políticas de API Gateway, Equilibrador de carga de red y basadas en recursos de AWS Identity and Access Management (IAM) dentro de sus cuentas de AWS como parte de su recurso de aplicación. Puede agregar servicios nuevos de manera transparente en una Cuenta de AWS única o en varias cuentas a un punto de conexión HTTP externo. Todos estos recursos están organizados dentro de Cuenta de AWS y se pueden personalizar y configurar después de la implementación.

Supongamos que los servicios de usuario y de carrito se implementan en dos cuentas distintas, como se muestra en el diagrama siguiente. Cuando utiliza Refactor Spaces, solo es necesario configurar el punto de conexión del servicio y la ruta. Refactor Spaces automatiza la integración entre API Gateway y Lambda y la creación de políticas de recursos de Lambda, para que pueda centrarse en refactorizar los servicios de manera segura fuera del monolito.

Implementación del patrón de higo estrangulador con AWS Migration Hub Refactor Spaces.

Para ver un tutorial en video sobre el uso de Refactor Spaces, consulte Refactor Apps Incrementally with AWS Migration Hub Refactor Spaces.

Taller

Referencias de blogs

Contenido relacionado