Descomposición de Monolitos: Patrones de Arquitectura aplicados en AWS. Primera Parte.
Tags: aws, microservices, pattern, architecture
Published: 2023-10-03 16:18:15

En el mundo del desarrollo de software, los monolitos han sido una arquitectura tradicional ampliamente utilizada. Un monolito es una aplicación en la que todas las funcionalidades se encuentran en un solo código base. A medida que las aplicaciones crecen, los monolitos pueden volverse difíciles de mantener y escalar. En este artículo, exploraremos qué es un monolito, sus ventajas e inconvenientes en comparación con los microservicios, y luego analizaremos los patrones de descomposición de monolitos disponibles y cómo aplicarlos en el entorno de Amazon Web Services (AWS).

## Contexto
Con el auge del cloud, se abre la posibilidad de poder desarrollar aplicaciones cloud native para sacar el máximo partido a la nube. ¿Pero qué pasa con aplicaciones que ya están desarrolladas? ¿Cómo las migramos y modernizamos para convertirlas en cloud native?

Lo primero es que hay diferentes estrategias para migrar a la nube, pero en este articulo nos vamos a centrar en la modernización de aplicaciones monolíticas que necesitan partir el monolito para poder escalar de forma diferente los diferentes módulos.

La elección entre usar un monolito o adoptar microservicios depende de varios factores, incluidos los requisitos de la aplicación, las características del equipo de desarrollo y las necesidades futuras del negocio.

## ¿Qué es un Monolito?
Un monolito es una arquitectura de software donde todas las funcionalidades de una aplicación están empaquetadas y ejecutadas dentro de un solo proceso. Esto significa que el código, la base de datos y cualquier otra dependencia están interconectados en un solo componente. Aunque los monolitos son fáciles de desarrollar e implementar al principio, pueden volverse complejos y difíciles de escalar a medida que la aplicación crece.

### Ventajas de los Monolitos:
Simplicidad: Los monolitos son más fáciles de desarrollar, probar y mantener, especialmente para aplicaciones pequeñas y de mediana envergadura.

Menos complejidad en despliegues: Como todo está empaquetado en una sola unidad, los despliegues pueden ser más sencillos, siempre que no haya conflictos entre las funcionalidades.

Fácil depuración: La depuración en un monolito es relativamente más simple, ya que todo el código está en un solo lugar.

Menos Requerimientos de Escalabilidad: Si no se prevé un crecimiento significativo en el número de usuarios o las demandas de rendimiento, un monolito puede ser suficiente. En estos casos, la complejidad de los microservicios puede no estar justificada.

### Inconvenientes de los Monolitos:
Dificultad para escalar: A medida que la aplicación crece, puede ser complicado escalar componentes específicos del monolito sin afectar toda la aplicación.

Acoplamiento: La naturaleza monolítica puede conducir a un alto acoplamiento entre las funcionalidades, lo que dificulta la modificación o adición de nuevas características sin afectar otras áreas.

Mayor tiempo de implementación: A medida que el monolito crece, el tiempo requerido para implementar nuevas características puede aumentar significativamente.

### Híbrido:
Es importante tener en cuenta que no es una elección "todo o nada". Algunas aplicaciones pueden beneficiarse de un enfoque híbrido, donde partes del sistema se mantienen como un monolito mientras que otras se descomponen en microservicios. Esta combinación puede ofrecer la simplicidad y la estabilidad de un monolito junto con la escalabilidad y la flexibilidad de los microservicios en áreas específicas de la aplicación.

Desde este primer momento AWS nos puede ayudar a planificar y organizar el proceso de migración usando AWS Migration Hub.

**AWS Migration Hub** brinda una ubicación central para recopilar datos de inventario de servidor y aplicación para la evaluación, la planificación y el rastreo de las migraciones a AWS. Migration Hub también puede agilizar la modernización de aplicaciones tras la migración.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1oqsmsw6fldcx2yox66j.jpg)

Para ilustrar los patrones que vamos a comentar para poder romper un monolito, partiremos de que ya tenemos nuestras cargas de trabajo en AWS, un servicio que nos ayuda a realizar un lift and shift de nuestra aplicación es AWS Application Migration Service.

AWS Application Migration Service minimiza los procesos manuales que consumen mucho tiempo y son propensos a errores al automatizar la conversión de sus servidores de origen para que se ejecuten de forma nativa en AWS. También simplifica la modernización de aplicaciones con opciones de optimización personalizadas e integradas.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/785o7sxq7a0tp6jgzbmt.jpg)

AWS Migration Hub está integrado con AWS Application Migration Service y puede utilizar la consola de AWS Migration Hub para supervisar los servidores que está migrando con AWS Application Migration Service.

Arquitectura de nuestro monolito actual:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tkpeodkr2jw16qmbq255.jpg)

## Patrones de Descomposición de Monolitos en AWS
### Patrón Strangler Fig:
El patrón Strangler Fig es un enfoque arquitectónico utilizado para modernizar gradualmente sistemas monolíticos, migrándolos hacia una arquitectura basada en microservicios. El término y la analogía provienen de Martin Fowler, un destacado experto en desarrollo de software y autor de renombre en el campo.

El patrón Strangler Fig fue introducido por Martin Fowler en su libro "Refactoring: Improving the Design of Existing Code". La analogía se inspira en la forma en que la planta trepadora "Strangler Fig" crece envolviendo gradualmente a un árbol huésped con sus raíces, finalmente reemplazando al árbol original por completo.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wcctjfc1hmfadxrfurs.jpg)
En términos de desarrollo de software, el patrón Strangler Fig implica reemplazar gradualmente partes de una aplicación monolítica existente con microservicios. Esto se hace a medida que se desarrollan y despliegan nuevos microservicios, los cuales, con el tiempo, van "estrangulando" y reemplazando las funcionalidades del monolito original.

Como hemos comentado, puedes usar este patrón si quieres migrar tu monolito de forma gradual sin tener impacto en la disponibilidad de la aplicación por lo que los usuarios finales no deberían ser impactados durante la migración. Puede también ocurrir que se necesite añadir una nueva funcionalidad y en vez de añadirla al monolito ya se cree como un microservicio independiente.

### Pasos para Implementar el Patrón Strangler Fig:

1. Añade la capa de proxy o Stranger facade
Añade el componente que será encargado de dirigir las peticiones al monolito o al microservicio que corresponda. Inicialmente, este proxy no hará nada más que un pass- through de todo el tráfico, sin modificar, a la aplicación monolítica.

5. Identificación de Componentes Para Modernizar
Identifica los componentes específicos del monolito que deseas modernizar. Pueden ser los módulos que necesitan escalar más, funcionalidades críticas que requieren mejoras o cualquier otra área que se beneficiaría de la transformación en un microservicio. Se puede usar diseño orientado a dominio (Domain driven design (DDD)) para este propósito.

9. Creación del Nuevo Microservicio
Desarrolla un nuevo microservicio que reemplace la funcionalidad del componente identificado en el paso anterior. Diseña el microservicio y extrae o desarrolla la funcionalidad que cumpla con los requisitos de la aplicación.

13. Configuración del enrutamiento, proxy o capa de adaptación
Implementa una capa de adaptación, proxy o enrutamiento, se puede denominar de muchas maneras, pero el objetivo es que permita canalizar las solicitudes del componente a modernizar hacia el nuevo microservicio en lugar del monolito existente. Esto puede involucrar la configuración de reglas de enrutamiento en un Load Balancer o la implementación de API Gateway para redirigir las solicitudes. Con esta estrategia pueden incrementar los tiempos de respuesta además de los costes de infraestructura ya que tendremos que mantener el monolito y los microservicios que se vayan creando, así como la fachada para enrutar las peticiones.

17. Implementación Gradual
Comienza a redirigir gradualmente las solicitudes del componente a modernizar hacia el nuevo microservicio utilizando la capa de adaptación o enrutamiento. Esto debe hacerse de manera controlada para minimizar el impacto en el funcionamiento general del sistema.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/20ftir1x4yxp27hogzi8.jpg)

Repite el Proceso

Repite los pasos anteriores para otros componentes que deseas modernizar. Cada nuevo microservicio debe seguir el mismo ciclo de desarrollo, pruebas y migración gradual. A medida que más componentes son reemplazados por microservicios, el monolito original se "estrangula" gradualmente.

Eliminación del Monolito

Una vez que todos los componentes hayan sido modernizados y reemplazados por microservicios, el monolito original puede ser eliminado de manera segura. Ahora tu aplicación habrá evolucionado hacia una arquitectura basada en microservicios, lo que permite una mayor escalabilidad, flexibilidad y mantenibilidad.

Una capacidad del servicio mencionado anteriormente de AWS, AWS Migration Hub, nos puede ayudar a realizar esta migración del monolito, se llama AWS Migration Hub Refactor Spaces.

AWS Migration Hub Refactor
AWS Migration Hub Refactor ayuda a construir y operar la infraestructura que vas a necesitar durante tu proceso de migración. Este servicio es especialmente útil si se hace uso de una infraestructura más compleja haciendo uso de múltiples cuentas de AWS.

Refactor Spaces te ayuda a seguir las buenas prácticas provisionando un aislamiento a nivel de cuentas (incluido el propio Refactor Spaces).

Por lo que la implementación de nuestro ejemplo usando la estrategia de multi-account con Refactor Spaces quedaría de la siguiente manera:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e539jzjx5azvvzp53tn4.jpg)
Como ves, esta arquitectura tiene tres cuentas:

1. Refactor Spaces management account donde Refactor Spaces configura el networking de la arquitectura cross-account y el enrutado de tráfico. (Se ha incluido la migración del frontend a un bucket de S3).
2. La cuenta de la aplicación monolítica.
3. La nueva cuenta para los microservicios
Cómo funciona:

En primer lugar, crea un entorno de Refactor Spaces en la cuenta elegida como propietario del entorno. A continuación, comparte el entorno con las otras dos cuentas. Después de compartir el entorno con otra cuenta, Refactor Spaces comparte automáticamente los recursos que crea dentro del entorno con las demás cuentas.

El entorno de refactor proporciona un networking unificado para todas las cuentas. Para ello, se configuran los siguientes servicios:

- AWS Transit Gateway
- AWS Resource Access Manager
- Network Load Balancer
- Amazon API Gateway
- VPCs y security groups


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c3h2zi5s1diors6hk5b4.jpg)

El entorno del refactor contiene la aplicación existente y los nuevos microservicios. Después de crear un entorno de refactor, crea una aplicación Refactor Spaces dentro del entorno. La aplicación Refactor Spaces contiene servicios y rutas y proporciona un único endpoint para exponer la aplicación al exterior.

Una aplicación admite el enrutamiento a servicios que se ejecutan en contenedores, serverless y Amazon Elastic Compute Cloud (Amazon EC2) con visibilidad pública o privada. Los servicios de una aplicación pueden tener uno de los dos tipos de endpoint: una URL (HTTP y HTTPS) en una VPC o en AWS Lambda.

Diagrama con contenedores en vez de lambdas:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4dshc7g9y38rr2j7j9w.jpg)
Una vez que una aplicación contiene un servicio, agrega una ruta predeterminada para dirigir todo el tráfico desde el proxy de la aplicación al servicio que representa la aplicación existente. A medida que se desarrolla o agrega nuevas capacidades en contenedores o serverless, agrega nuevos servicios y rutas para redirigir el tráfico a los nuevos servicios. Como ya te habrás dado cuenta, esto sería una implementación del componente enrutador/proxy o strangler facade, además este servicio gestiona toda la infraestructura que se necesita para poder implementar este patrón.

Durante el proceso de migración, nos podemos encontrar que desde el monolito también se realiza una llamada al servicio que acabamos de migrar. En este caso necesitamos exponer la funcionalidad dentro del monolito para que se pueda llamar desde el nuevo microservicio. Pero, ¿cómo evitamos que el cambio del modelo de dominio o los datos no impacte en el diseño del microservicio?

Para este problema podemos usar otro patrón de arquitectura llamado Anticorruption Layer.

Sobre este patrón y otros más, hablaremos en la segunda parte de este artículo: Descomposición de Monolitos: Patrones de Arquitectura aplicados en AWS (Segunda Parte)

Links y referencias:
https://martinfowler.com/bliki/StranglerFigApplication.html

https://docs.aws.amazon.com/es_es/prescriptive-guidance/latest/cloud-design-patterns/strangler-fig.html

https://docs.aws.amazon.com/migrationhub/index.html

https://docs.aws.amazon.com/migrationhub-refactor-spaces/latest/userguide/what-is-mhub-refactor-spaces.html

https://catalog.us-east-1.prod.workshops.aws/workshops/f2c0706c-7192-495f-853c-fd3341db265a/en-US

https://catalog.us-east-1.prod.workshops.aws/workshops/aeb7ce2c-b1de-470e-8371-0268f6f21b79/en-US

https://www.oreilly.com/library/view/monolith-to-microservices/9781492047834/