Editor – Esta serie de siete artículos ya está completa:
- Introducción a los microservicios
- Creación de microservicios: uso de una puerta de enlace de API
- Creación de microservicios: comunicación entre procesos en una arquitectura de microservicios
- Descubrimiento de servicios en una arquitectura de microservicios
- Gestión de datos basada en eventos para microservicios
- Cómo elegir una estrategia de despliegue de microservicios
- Refactorización de un monolito en microservicios (este artículo)
También puede descargar la colección completa de artículos, junto con información sobre cómo implementar microservicios con NGINX Plus, en formato de libro electrónico: «Microservicios: del diseño a la implementación». Consulte también nuestra serie sobre la arquitectura de referencia de microservicios y la página de soluciones de microservicios.
Este es el séptimo y último artículo de mi serie sobre la creación de aplicaciones con microservicios. El primer artículo presenta el patrón de arquitectura de microservicios y analiza las ventajas y desventajas de utilizar microservicios. Los siguientes artículos abordan distintos aspectos de la arquitectura de microservicios: el uso de una puerta de enlace de API<.htmla>, la comunicación entre procesos, el descubrimiento de servicios, la gestión de datos basada en eventos y el despliegue de microservicios. En este artículo, analizamos estrategias para migrar una aplicación monolítica a microservicios.
Espero que esta serie de artículos le haya permitido comprender bien la arquitectura de microservicios, sus ventajas e inconvenientes y cuándo utilizarla. Quizá la arquitectura de microservicios sea una buena opción para su organización.
Sin embargo, es bastante probable que esté trabajando en una aplicación monolítica grande y compleja. Su experiencia diaria al desarrollar e implementar la aplicación es lenta y frustrante. Los microservicios parecen un nirvana lejano. Afortunadamente, existen estrategias que puede utilizar para escapar del infierno monolítico. En este artículo, explico cómo refactorizar gradualmente una aplicación monolítica para convertirla en un conjunto de microservicios.
Descripción general de la refactorización hacia microservicios
El proceso de transformar una aplicación monolítica en microservicios es una forma de modernización de aplicaciones. Es algo que los desarrolladores llevan décadas haciendo. Por tanto, hay algunas ideas que podemos reutilizar al refactorizar una aplicación para convertirla en microservicios.
Una estrategia que no se debe utilizar es la reescritura «Big Bang». Consiste en centrar todos los esfuerzos de desarrollo en crear desde cero una nueva aplicación basada en microservicios. Aunque pueda parecer atractiva, es extremadamente arriesgada y probablemente acabará en fracaso. Como Martin Fowler dijo al parecer, «lo único que garantiza una reescritura Big Bang es un gran estallido».
En lugar de realizar una reescritura «Big Bang», debes refactorizar gradualmente tu aplicación monolítica. Poco a poco, creas una nueva aplicación compuesta por microservicios y la ejecutas junto con tu aplicación monolítica. Con el tiempo, la funcionalidad implementada por la aplicación monolítica se reduce hasta que desaparece por completo o se convierte en un microservicio más. Esta estrategia es comparable a revisar el coche mientras conduces por la autopista a 70 mph: todo un reto, pero mucho menos arriesgado que intentar una reescritura «Big Bang».
Martin Fowler denomina esta estrategia de modernización de aplicaciones aplicación estranguladora. El nombre proviene de la liana estranguladora (también conocida como higuera estranguladora) que se encuentra en las selvas tropicales. Una liana estranguladora crece alrededor de un árbol para alcanzar la luz solar por encima del dosel del bosque. A veces, el árbol muere y deja una liana con forma de árbol. La modernización de aplicaciones sigue el mismo patrón. Crearemos una nueva aplicación compuesta por microservicios alrededor de la aplicación heredada, que acabará desapareciendo.

Veamos distintas estrategias para hacerlo.
Estrategia 1 – Deja de cavar
La ley de los agujeros dice que, cuando estás en un agujero, debes dejar de cavar. Es un consejo excelente cuando tu aplicación monolítica se ha vuelto inmanejable. En otras palabras, debes dejar de ampliar el monolito. Esto significa que, al implementar nuevas funcionalidades, no debes añadir más código al monolito. La idea principal de esta estrategia es, en cambio, incluir ese nuevo código en un microservicio independiente. El siguiente diagrama muestra la arquitectura del sistema tras aplicar este enfoque.

Además del nuevo servicio y del monolito heredado, hay otros dos componentes. El primero es un enrutador de solicitudes que gestiona las solicitudes entrantes (HTTP). Es similar a la puerta de enlace de API descrita en un artículo anterior<.htmla>. El enrutador envía al nuevo servicio las solicitudes correspondientes a las nuevas funcionalidades. Las solicitudes heredadas se dirigen al monolito.
El otro componente es el código de enlace, que integra el servicio con el monolito. Un servicio rara vez existe de forma aislada y a menudo necesita acceder a datos que pertenecen al monolito. El código de enlace, que se encuentra en el monolito, en el servicio o en ambos, se encarga de la integración de los datos. El servicio utiliza el código de enlace para leer y escribir datos que pertenecen al monolito.
Hay tres estrategias que un servicio puede utilizar para acceder a los datos del monolito:
- Invocar una API remota proporcionada por el monolito
- Acceder directamente a la base de datos del monolito
- Mantener su propia copia de los datos, sincronizada con la base de datos del monolito
El código de enlace se denomina a veces capa anticorrupción. Esto se debe a que el código de enlace evita que el servicio, que tiene su propio modelo de dominio impoluto, se contamine con conceptos del modelo de dominio del monolito heredado. El código de enlace traduce entre ambos modelos. El término «capa anticorrupción» apareció por primera vez en el libro de lectura obligada Domain Driven Design de Eric Evans y posteriormente se desarrolló en un libro blanco. Desarrollar una capa anticorrupción puede ser una tarea nada trivial. Sin embargo, crearla es esencial si quieres escapar progresivamente del infierno monolítico.
Implementar nuevas funcionalidades como un servicio ligero ofrece varias ventajas. Evita que el monolito se vuelva aún más inmanejable. El servicio se puede desarrollar, desplegar y escalar de forma independiente del monolito. Cada nuevo servicio que se crea permite aprovechar las ventajas de la arquitectura de microservicios.
Sin embargo, este enfoque no resuelve en absoluto los problemas del monolito. Para solucionarlos, es necesario descomponer el monolito. Veamos algunas estrategias para hacerlo.
Estrategia 2 – Separar el frontend y el backend
Una estrategia para reducir la aplicación monolítica consiste en separar la capa de presentación de las capas de lógica de negocio y acceso a datos. Una aplicación empresarial típica consta de al menos tres tipos diferentes de componentes:
- Capa de presentación – Componentes que gestionan solicitudes HTTP e implementan una API (REST) o una interfaz de usuario web basada en HTML. En una aplicación con una interfaz de usuario sofisticada, la capa de presentación suele contener una cantidad considerable de código.
- Capa de lógica de negocio – Componentes que constituyen el núcleo de la aplicación e implementan las reglas de negocio.
- Capa de acceso a datos: componentes que acceden a componentes de infraestructura, como bases de datos y brókeres de mensajes.
Normalmente existe una separación clara entre la lógica de presentación, por un lado, y la lógica de negocio y de acceso a datos, por otro. La capa de negocio dispone de una API de granularidad gruesa compuesta por una o varias fachadas que encapsulan los componentes de la lógica de negocio. Esta API constituye un punto de separación natural por el que se puede dividir el monolito en dos aplicaciones más pequeñas. Una aplicación contiene la capa de presentación. La otra contiene la lógica de negocio y de acceso a datos. Tras la división, la aplicación de lógica de presentación realiza llamadas remotas a la aplicación de lógica de negocio. El siguiente diagrama muestra la arquitectura antes y después de la refactorización.

Dividir un monolito de esta forma ofrece dos ventajas principales. Permite desarrollar, desplegar y escalar las dos aplicaciones de manera independiente. En concreto, permite a los desarrolladores de la capa de presentación iterar rápidamente sobre la interfaz de usuario y realizar fácilmente pruebas A/B, por ejemplo. Otra ventaja de este enfoque es que expone una API remota a la que pueden llamar los microservicios que desarrolle.
Sin embargo, esta estrategia solo es una solución parcial. Es muy probable que una de las aplicaciones, o ambas, se conviertan en monolitos inmanejables. Debe utilizar la tercera estrategia para eliminar el monolito o los monolitos restantes.
Estrategia 3 – Extraer servicios
La tercera estrategia de refactorización consiste en convertir los módulos existentes dentro del monolito en microservicios independientes. Cada vez que se extrae un módulo y se convierte en un servicio, el monolito se reduce. Una vez que se hayan convertido suficientes módulos, el monolito dejará de ser un problema. O bien desaparecerá por completo, o bien se reducirá lo suficiente como para ser simplemente un servicio más.
Priorización de los módulos que se convertirán en servicios
Una aplicación monolítica grande y compleja consta de decenas o cientos de módulos, todos ellos susceptibles de ser extraídos. Decidir qué módulos convertir primero suele ser complicado. Un buen enfoque consiste en empezar por unos pocos módulos que sean fáciles de extraer. Esto le permitirá adquirir experiencia tanto con los microservicios en general como con el proceso de extracción en particular. Después, debería extraer los módulos que le aporten el mayor beneficio.
Convertir un módulo en un servicio suele llevar mucho tiempo. Conviene priorizar los módulos en función del beneficio que se obtendrá. Por lo general, resulta beneficioso extraer los módulos que cambian con frecuencia. Una vez convertido un módulo en un servicio, se puede desarrollar y desplegar de forma independiente del monolito, lo que agilizará el desarrollo.
También resulta beneficioso extraer módulos cuyos requisitos de recursos difieran significativamente de los del resto del monolito. Por ejemplo, puede ser útil convertir en un servicio un módulo que tenga una base de datos en memoria, ya que así podrá desplegarse en hosts con grandes cantidades de memoria. Del mismo modo, puede merecer la pena extraer módulos que implementen algoritmos con un alto coste computacional, puesto que el servicio podrá desplegarse en hosts con muchas CPU. Al convertir en servicios los módulos con requisitos de recursos específicos, puede facilitar considerablemente el escalado de su aplicación.
A la hora de determinar qué módulos extraer, conviene buscar límites de grano grueso ya existentes (también conocidos como puntos de separación). Estos facilitan y abaratan la conversión de módulos en servicios. Un ejemplo de este tipo de límite es un módulo que solo se comunica con el resto de la aplicación mediante mensajes asíncronos. Convertir dicho módulo en un microservicio puede resultar relativamente sencillo y económico.
Cómo extraer un módulo
El primer paso para extraer un módulo consiste en definir una interfaz de grano grueso entre el módulo y el monolito. Lo más probable es que se trate de una API bidireccional, ya que el monolito necesitará datos que pertenecen al servicio y viceversa. Implementar una API de este tipo suele ser complicado debido a las dependencias entrelazadas y a los patrones de interacción de grano fino entre el módulo y el resto de la aplicación. La lógica de negocio implementada mediante el patrón Modelo de dominio resulta especialmente difícil de refactorizar debido a las numerosas asociaciones entre las clases del modelo de dominio. A menudo será necesario realizar cambios importantes en el código para romper estas dependencias. El siguiente diagrama muestra la refactorización.
Una vez implementada la interfaz de grano grueso, el siguiente paso es convertir el módulo en un servicio independiente. Para ello, debes escribir código que permita que el monolito y el servicio se comuniquen mediante una API que utilice un mecanismo de comunicación entre procesos (IPC). El siguiente diagrama muestra la arquitectura antes, durante y después de la refactorización.

En este ejemplo, el módulo Z es el módulo candidato para su extracción. Sus componentes son utilizados por el módulo X y, a su vez, este utiliza el módulo Y. El primer paso de la refactorización consiste en definir un par de API de granularidad gruesa. La primera interfaz es una interfaz de entrada que el módulo X utiliza para invocar al módulo Z. La segunda es una interfaz de salida que el módulo Z utiliza para invocar al módulo Y.
El segundo paso de la refactorización convierte el módulo en un servicio independiente. Las interfaces de entrada y salida se implementan mediante código que utiliza un mecanismo de IPC. Lo más probable es que tenga que crear el servicio combinando el Módulo Z con un framework de chasis para microservicios que gestione aspectos transversales como el descubrimiento de servicios.
Una vez extraído un módulo, dispondrá de otro servicio que podrá desarrollarse, desplegarse y escalarse de forma independiente del monolito y de cualquier otro servicio. Incluso puede reescribir el servicio desde cero; en este caso, el código de la API que integra el servicio con el monolito se convierte en una capa anticorrupción que traduce entre los dos modelos de dominio. Cada vez que extrae un servicio, da un paso más hacia los microservicios. Con el tiempo, el monolito se reducirá y dispondrá de un número cada vez mayor de microservicios.
Resumen
El proceso de migrar una aplicación existente a microservicios es una forma de modernización de aplicaciones. No debe adoptar los microservicios reescribiendo su aplicación desde cero. En su lugar, debe refactorizarla de forma gradual para convertirla en un conjunto de microservicios. Puede utilizar tres estrategias: implementar las nuevas funcionalidades como microservicios; separar los componentes de presentación de los componentes de lógica de negocio y acceso a datos; y convertir en servicios los módulos existentes del monolito. Con el tiempo, aumentará el número de microservicios, así como la agilidad y la velocidad de su equipo de desarrollo.
Editor – Esta serie de siete artículos ya está completa:
- Introducción a los microservicios
- Creación de microservicios: uso de una puerta de enlace de API
- Creación de microservicios: comunicación entre procesos en una arquitectura de microservicios
- Descubrimiento de servicios en una arquitectura de microservicios
- Gestión de datos basada en eventos para microservicios
- Cómo elegir una estrategia de despliegue de microservicios
- Refactorización de un monolito en microservicios (este artículo)
También puede descargar la colección completa de artículos, junto con información sobre cómo implementar microservicios con NGINX Plus, en formato de libro electrónico: «Microservicios: del diseño a la implementación». Consulte también nuestra serie sobre la arquitectura de referencia de microservicios y la página de soluciones de microservicios.
El bloguero invitado Chris Richardson es el fundador de la plataforma original CloudFoundry.com, una de las primeras PaaS (plataforma como servicio) de Java para Amazon EC2. Actualmente asesora a organizaciones para mejorar la forma en que desarrollan e implementan aplicaciones. También publica regularmente sobre microservicios en https://microservices.io
Sobre el autor
Artículos relacionados del blog
Únete a F5 NGINX en AppWorld 2024
Descubre las numerosas razones para asistir al evento insignia de F5, que incluirá un emocionante anuncio sobre la cartera de productos NGINX.
Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source
La función de diccionario compartido permite configurar NGINX sin necesidad de reiniciarlo al renovar los certificados SSL/TLS.
Exposición de APIs en Kubernetes
La unidad 2 de Microservices March 2022 se centra en cómo exponer tus API en Kubernetes. Aquí tienes enlaces a los recursos que necesitas: la retransmisión en directo del 14 de marzo, artículos de blog y otros recursos de preparación, y el laboratorio práctico «Protege las API de Kubernetes mediante la limitación de solicitudes».
NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto
Hoy, en NGINX Sprint 2.0, anunciamos cómo respaldaremos nuestra visión del futuro de las aplicaciones modernas con una mayor presencia en la comunidad de código abierto, incluida la participación en el proyecto comunitario Kubernetes, nuevos proyectos de software de código abierto y la arquitectura de referencia para aplicaciones modernas.
Presentamos NGINX Plus R22
NGINX Plus R22 incorpora nuevas funcionalidades, como la compatibilidad con OCSP para verificar la revocación de certificados en TLS mutuo, el uso de varios proveedores de identidad OIDC en una configuración, el seguimiento en tiempo real de la limitación de solicitudes y conexiones en el panel de NGINX Plus y mejoras en NGINX JavaScript.
Presentamos NGINX Plus R17
NGINX Plus R17 incorpora compatibilidad con la limitación de velocidad en dos fases y con TLS 1.3, la versión más reciente del protocolo Transport Layer Security. La configuración de OpenID Connect es más sencilla y NGINX ModSecurity WAF es el doble de rápido que antes. También se ha actualizado el módulo JavaScript de NGINX.