Foto de Markus Spiske en Pexel
Como arquitecto a menudo me realizan esta pregunta: «¿Cómo puedo romper un monolito en microservicios?» Y casi siempre termino hablando de estos 3 enfoques más una técnica facilitadora.
La transición de una arquitectura monolítica a una de microservicios puede ser todo un reto, ¡pero los beneficios son increíbles! Al romper ese monolito en microservicios, obtendrás flexibilidad, escalabilidad y un desarrollo ágil en toda regla. En este artículo, te voy a mostrar tres enfoques efectivos para descomponer tu aplicación monolítica en microservicios, permitiéndote sacarle el máximo provecho a esta arquitectura moderna y poderosa. Vamos a descubrir cómo puedes hacer una transición exitosa hacia una arquitectura de microservicios más ágil y eficiente a través de la refactorización gradual, la identificación de dominios y un enfoque basado en capacidades. ¡Prepárate para convertir tu aplicación monolítica en un conjunto de servicios autónomos y altamente escalables!
Patrones de descomposición
Cuando te enfrentas a la tarea de transformar una aplicación de una arquitectura monolítica a una de microservicios, o cuando estás creando una aplicación nueva con una arquitectura de microservicios, el mayor desafío es cómo dividir tu aplicación en esos microservicios. Rascar la superficie de la aplicación y dividirla en microservicios requiere seguir ciertas estrategias para aislar lógicamente las funcionalidades de la aplicación en servicios autónomos, y también definir una estrategia de acceso a datos y comunicación.
Descomposición por capacidades empresariales
En esta estrategia de descomposición, lo que haces es dividir tu aplicación monolítica en microservicios autónomos basándote en las capacidades empresariales que has identificado. Esta descomposición se basa en conceptos de modelado de la arquitectura empresarial, donde una capacidad empresarial es lo que tu empresa hace para generar valor. Por ejemplo, en una aplicación de tarjetas de crédito, puedes tener capacidades empresariales como el marketing de productos de tarjetas de crédito, la apertura de cuentas de tarjetas de crédito, la activación de tarjetas y la generación de estados de cuenta.
A medida que pasa el tiempo, la forma en que haces negocios puede cambiar, pero lo que no cambia mucho es tu negocio principal. Por eso, al descomponer tu aplicación utilizando las capacidades empresariales, no estás cambiando esas capacidades en sí, aunque sí puede cambiar la forma en que se realizan los negocios. Por ejemplo, hoy puedes estar recibiendo solicitudes de tarjetas de crédito en papel, pero mañana podrías comenzar a recibirlas en línea, a través de una app móvil o por teléfono. Sin embargo, la forma en que se abre una cuenta de tarjeta de crédito o cómo se activa la tarjeta en el backend no cambia. Por lo tanto, al descomponer tu aplicación utilizando las capacidades empresariales, se ajusta perfectamente a la forma en que tu negocio está funcionando actualmente, por lo que los usuarios empresariales no sentirán ningún cambio.
Para llevar a cabo la descomposición de tu aplicación en torno a las capacidades empresariales, necesitarás un equipo que entienda las diferentes unidades empresariales de tu organización, así como expertos en el tema que comprendan todas esas capacidades empresariales y puedan ayudarte a realizar la descomposición.
En esta estrategia, identificar una capacidad empresarial significa comprender el propósito de hacer algo desde el punto de vista empresarial. Por ejemplo, si piensas que crear un cliente es una capacidad empresarial y quieres crear un servicio para eso, en realidad no estarías en lo correcto, porque nuestro negocio no es crear clientes, sino proporcionar tarjetas de crédito a los clientes. En cambio, podemos tener algunos servicios que respalden nuestras capacidades empresariales. Por lo tanto, debes tener mucho cuidado al identificar los microservicios basándote en este patrón de descomposición.
En el siguiente diagrama, he desglosado una aplicación monolítica de tarjetas de crédito en diferentes capacidades empresariales que se encuentran en cualquier aplicación de este tipo. Por ejemplo, al abrir una cuenta de tarjeta de crédito para un cliente, no importa cómo se abra esa cuenta, ya sea a través de un empleado de un banco en una sucursal, un cliente en tu sitio web, una app móvil o un cliente que llama por teléfono para solicitar una tarjeta de crédito. No importa cómo se abrió la cuenta de tarjeta de crédito, ¡ese servicio cumple con la capacidad empresarial!
Otra función de esta aplicación monstuosa es activar las tarjetas de crédito para que se puedan usar en las compras. Para eso, la descomponemos en un microservicio dedicado a la activación de tarjetas. No importa si se activa una tarjeta por teléfono o a través del sitio web, este servicio se encarga de hacerlo.
Usar esta estrategia para descomponer una aplicación grande en microservicios tiene varias ventajas:
- Te da una arquitectura de microservicios muy estable, especialmente si las capacidades del negocio son estables en general.
- Cada componente de la arquitectura puede evolucionar con el tiempo a medida que cambian aspectos del negocio, pero la arquitectura en sí no se altera, lo que da mucha estabilidad.
- Además, este enfoque fomenta equipos de desarrollo interfuncionales y centrados en brindar valor al negocio, en lugar de enfocarse en características técnicas. Eso facilita la comunicación y colaboración entre los equipos del proyecto.
Sin embargo, también hay algunas desventajas en este enfoque:
- La arquitectura de la aplicación estará muy ligada al modelo de negocio, lo que puede generar limitaciones si el modelo de negocio cambia en gran medida.
- Con el tiempo, las personas se acostumbran a realizar las actividades comerciales de una manera específica, y si hay alguna excepción en ese proceso, eso se verá reflejado en la arquitectura resultante.
- Este enfoque requiere una comprensión sólida de toda la empresa, y puede ser todo un desafío identificar las capacidades y servicios empresariales adecuados.
Descomposición por subdominios
En esta estrategia, seguimos los principios del diseño enfocado en el mundo real. Básicamente, se trata de dividir tu negocio en partes más pequeñas y crear microservicios para cada una de ellas. Cada microservicio se enfoca en un subdominio específico y trabaja dentro de un contexto bien definido.
Pero, ¿qué significa todo eso? Bueno, piensa en tu negocio como un enorme rompecabezas. Cada parte del rompecabezas representa un subdominio, que es básicamente una parte diferente de tu negocio. Por ejemplo, si tienes una tienda en línea, tendrías subdominios para la gestión de proveedores, la atención al cliente, los pedidos y así sucesivamente.
La idea es descomponer tu aplicación en microservicios que se encarguen de cada uno de estos subdominios. Cada microservicio tiene su propio ámbito de responsabilidad y se centra en manejar todo lo relacionado con su subdominio en particular. Por ejemplo, el microservicio de gestión de proveedores se encargaría de toda la logística relacionada con los proveedores, mientras que el microservicio de atención al cliente se enfocaría en brindar soporte y resolver problemas para los clientes.
La ventaja de esta estrategia es que puedes abordar cada subdominio de manera independiente y con equipos especializados en cada área. Esto significa que cada microservicio puede evolucionar de forma autónoma, sin afectar a los demás. Además, esta descomposición te permite tener una arquitectura más flexible y escalable, ya que puedes agregar, modificar o eliminar microservicios según las necesidades cambiantes de tu negocio.
Entonces, en resumen, la descomposición por subdominios es como construir un rompecabezas, dividiendo tu negocio en partes más pequeñas y creando microservicios para manejar cada una de esas partes. Es una forma eficiente de organizar y escalar tu aplicación, centrándote en las necesidades específicas de cada subdominio.
Hay diferentes categorías para los subdominios, según su relación con el negocio de tu empresa:
- Núcleo: Estos son los subdominios más importantes, la esencia misma de tu aplicación y lo que realmente impulsa tu negocio.
- De apoyo: Estos subdominios están relacionados con tu negocio, pero no son el centro de atención principal.
- Genéricos: Estos subdominios son más generales y pueden aplicarse a diferentes áreas, no son específicos de tu negocio en particular.
Pero espera, antes de seguir adelante, necesitamos entender qué es un «contexto delimitado». En pocas palabras, un contexto delimitado es como una burbuja que define un área específica donde se aplica un conjunto de reglas y un modelo de negocio particular. Imagina que tienes un modelo de negocio de seguros y dentro de ese modelo tienes diferentes contextos, como el seguro de automóviles y el seguro de vida. Cada uno de estos contextos tiene su propio significado y reglas dentro del dominio de los seguros.
Ahora, sé que esto puede sonar confuso, así que echemos un vistazo al siguiente diagrama para que lo veas más claro.
Mira este dibujo: en la parte de arriba tienes el problema en general, a la izquierda está el negocio y a la derecha ves cómo ese negocio se divide en problemitas más chiquitos llamados subdominios. El negocio es el problema a resolver, mientras que el modelo de negocio es la forma en que se resuelve, y así como un subdominio es una parte del problema, un contexto acotado es una parte de la solución. Ahora que ya ves como funciona este patrón de descomposición, te pongo un ejemplo para que se vea más claro.
En el ejemplo anterior, rompimos una aplicación de tienda online en pedacitos usando una técnica llamada subdominios de negocio. Identificamos subdominios como clientes, vendedores, pedidos, productos, inventario, y así. Algunos de estos subdominios son súper importantes para nuestro negocio, otros están relacionados y otros son más genéricos.
Aquí está el truco: cualquier cosa relacionada con los clientes, como crear un cliente, activarlo, actualizarlo, borrarlo, cambiar su dirección, y así, todo eso cae en el subdominio de clientes. Del mismo modo, cualquier cosa relacionada con los vendedores se agrupa en el subdominio de gestión de vendedores. ¿Me sigues? La idea es identificar las partes clave de nuestro negocio y luego dividirlas en subdominios. Y luego, todo lo relacionado con cada subdominio dentro de un área específica se maneja por su propio microservicio.
Descomposición por transacciones
Cuando pasas de una aplicación monolítica a una arquitectura de microservicios, te encuentras con un problema interesante: ¿cómo manejas las transacciones que involucran varios microservicios? Resulta que en los microservicios, la funcionalidad de tu negocio está repartida entre diferentes servicios, y cada uno tiene su propia base de datos. Esto significa que no puedes manejar las transacciones directamente a nivel de base de datos.
Voy a darte un ejemplo para que lo entiendas mejor. Supongamos que tienes una aplicación de comercio electrónico que has dividido en microservicios usando la estrategia de subdominio de negocio. Ahora, un cliente realiza un pedido de uno de tus productos. ¿Qué pasa entonces? Pues necesitas procesar el pago, actualizar el inventario, enviar una confirmación al cliente y, al mismo tiempo, enviar el pedido para su cumplimiento. Pero aquí está la trampa: todas estas acciones están repartidas en diferentes microservicios, como la gestión de inventario, la gestión de pedidos, los servicios de notificación, y así sucesivamente. Entonces, ¿cómo te aseguras de que todos estos microservicios hagan su trabajo correctamente o puedan deshacer la transacción si algo sale mal?
Para manejar esta situación, tienes dos opciones:
- Puedes implementar un patrón de confirmación en dos fases a nivel de base de datos. 2PC (two-phase commit protocol) es una técnica para implementar una transacción a través de múltiples bases de datos, donde los cambios son confirmados o revertidos desde todos los componentes involucrados.
- Puedes utilizar el patrón SAGA (Acceso Segregado de Atomicidad Global, por sus siglas en inglés) para superar estas limitaciones de los microservicios. Sin embargo, el patrón de confirmación en dos fases no es muy recomendable para la arquitectura de microservicios debido a problemas de rendimiento.
Este patrón es de lo más útil cuando la rapidez es un aspecto crucial en tu aplicación. ¿Por qué? Pues resulta que cuando tienes que comunicarte con otros servicios y coordinar una transacción, se tarda un tiempo considerable debido a la latencia de la red. Pero con este patrón, ¡puedes reducir esa latencia al mínimo! ¿Cómo? Sencillo, ¡colocando toda tu transacción dentro del mismo servicio! De esta manera, no tienes que hacer llamadas adicionales a otros servicios.
Vamos a utilizar el ejemplo, una aplicación de incidencvias para entender mejor cómo descomponer una aplicación monolítica basada en transacciones. Ahora, ten en cuenta que las plataformas de suelen ser mucho más complejas y tienen muchas más funciones y requisitos. Pero por ahora, nos centraremos en un conjunto pequeño de funciones, como la creación de la incidencia, tratamiento de la incidencia, y algunas funciones de consulta, de stock de componentes para la reparación y selección de un operario para la reparación.
Mira el diagrama a continuación. Hemos descompuesto esta aplicación en servicios distintos, cada uno basado en una transacción específica. Por ejemplo, el servicio de registro de clientes se encarga de todo el proceso complejo de recopilar los datos personales de los clientes, realizar verificaciones del id del aparato para reparar, validar la garantia, asociar el aparato al cliente, crear un perfil de cliente y enviar correo de apartura de incidencia. ¡Es un trabajo que se lleva a cabo en un abrir y cerrar de ojos dentro de este servicio dedicado!
Hay otro servicio en esta aplicación que se encarga de las ordenes de trabajo. Imagina que quieres crear una orden. Pues bien, tienes que hacer la orden, cumplimentarla, mirar el stock de piezas, herramientas necesarias, operarios, calendario de entra en reparación, recibir una confirmación de la operación, y así sucesivamente. Todo esto ocurre dentro de una sola transacción, y todas esas tareas son llevadas a cabo por el mismo servicio.
Del mismo modo, hay otros servicios dedicados, generar informes y enviar notificaciones del estado al cliente. Cada uno de estos servicios realiza esas transacciones por su cuenta, sin necesidad de depender de otros servicios externos.
Esta estrategia de descomponer tu aplicación monolítica tiene varias ventajas interesantes:
- Te da una respuesta más rápida en comparación con otras estrategias, ya que no hay que hacer llamadas remotas a múltiples servicios para completar una función de negocio. Esto ahorra tiempo en la comunicación entre servicios y hace que el tiempo de respuesta global sea más rápido.
- Mejora la disponibilidad de las funciones de negocio, ya que todas las dependencias necesarias para esa transacción están dentro del mismo servicio. Esto aumenta la disponibilidad general del servicio.
- No tienes que preocuparte por la consistencia de los datos, ya que todos los cambios relacionados con una función de negocio se realizan dentro de la misma base de datos de servicios. Además, si necesitas revertir una transacción, es fácil de implementar.
- No llegas al estado de nano-servicios, que es muy complicado de orquestar.
Pero también hay algunas desventajas a considerar:
- Diseñar los servicios en torno a transacciones de negocio puede llevar a tener servicios que son, en sí mismos, como pequeñas aplicaciones monolíticas y entramos en la definición difusa de SOA o monolito distribuido (que da para otro artículo).
- Al desplegar un microservicio, puede haber dos versiones diferentes en juego, lo que puede causar problemas en servicios basados en transacciones.
- La complejidad del servicio aumenta y es posible que necesites un equipo más grande para gestionarlos, lo que va en contra de los beneficios de tener una arquitectura de microservicios.
- Un servicio puede llegar a ser muy grande, ya que algunas transacciones de negocio tienen múltiples dominios y dependencias entre sí.
- En este patrón, es posible que un microservicio tenga que hacer más de una cosa, aunque no estén directamente relacionadas desde el punto de vista del dominio, pero son parte de la misma transacción de negocio. Esto viola el principio de responsabilidad única.
Esta estrategia de descomposición nos ayuda a lograr tiempos de respuesta similares a los de una aplicación monolítica, aprovechando al máximo la arquitectura de microservicios.
Pero mi recomendación es evitar a toda costa esta decisión, debes saber que existe y debes saber los pros y contras para poder ofrecere más de 2 soluciones a un cliente.
Conclusiones
- La transición de una arquitectura monolítica a una de microservicios puede ser un desafío, pero los beneficios son significativos, como flexibilidad, escalabilidad y desarrollo ágil.
- La descomposición de una aplicación monolítica en microservicios se puede lograr mediante enfoques como la descomposición por capacidades empresariales, la descomposición por subdominios y la descomposición por transacciones.
- La descomposición por capacidades empresariales permite adaptar la aplicación a la forma en que el negocio está funcionando actualmente y fomenta la estabilidad y colaboración entre equipos.
- La descomposición por subdominios divide el negocio en partes más pequeñas, creando microservicios para cada una de ellas, lo que permite abordar cada subdominio de manera independiente y tener una arquitectura flexible y escalable.
- La descomposición por transacciones aborda el desafío de manejar transacciones que involucran varios microservicios, ofreciendo opciones como el patrón de confirmación en dos fases y el patrón SAGA.
- Cada enfoque tiene sus ventajas y desventajas, y es importante comprender las necesidades y características específicas de la aplicación y del negocio al elegir una estrategia de descomposición.
Y añado EventStorming aplicado a microservicios:
Además de los enfoques mencionados en el artículo, una técnica adicional que puede ser útil al descomponer una aplicación monolítica en microservicios es el EventStorming. EventStorming es una técnica de modelado colaborativo que permite visualizar y comprender el flujo de eventos en un sistema. Al aplicar EventStorming a la arquitectura de microservicios, se pueden identificar eventos de dominio relevantes y utilizarlos como punto de partida para definir los límites de los microservicios.
El EventStorming puede ayudar a comprender cómo los diferentes componentes interactúan y se comunican entre sí a través de eventos de dominio. Al mapear estos eventos, se pueden identificar las fronteras de los microservicios y definir los límites contextuales adecuados. Esto facilita la división de la aplicación monolítica en microservicios cohesivos y acoplados débilmente, lo que favorece la escalabilidad y el desarrollo ágil.
Al aplicar EventStorming a la descomposición de microservicios, es importante involucrar a expertos en el dominio y a los equipos de desarrollo relevantes. La colaboración y la comprensión compartida del dominio empresarial son fundamentales para identificar los eventos de dominio adecuados y diseñar una arquitectura de microservicios efectiva.
EventStorming
Puede complementar los enfoques de descomposición mencionados en el artículo al proporcionar una técnica visual y colaborativa para identificar eventos de dominio y definir límites de microservicios en la arquitectura.