Introducción: Desafíos de los Monolitos y Ventajas de los Microservicios con Spring

En la primera parte discutimos los problemas inherentes a los sistemas monolitos y cómo los microservicios con spring proporcionas soluciones efectivas. Ahora, exploraremos en detalle cómo implementar y gestionar la comunicación y el almacenamiento de datos entre microservicios utilizando Java y Spring, destacando la modularidad y escalabilidad que esta arquitectura permite.

Desafíos de almacenar datos en microservicios con Spring

El primer gran desafío que viene con los microservicios es almacenar datos. Esto es más difícil de lo que piensas. Lo más importante es que no quieres tener solo una base de datos para todos los servicios. Así que evitaremos esto:

¿Por qué es problemático usar una sola base de datos para todo?

Utilizar una única base de datos para todos los componentes de una aplicación, como Productos, Pagos, Entregas y Órdenes, puede llevar a varios problemas:

    1. Acoplamiento Estrecho: Cuando todos los servicios dependen de una base de datos central, cualquier cambio en la estructura de la base de datos puede tener un impacto desproporcionado. Por ejemplo, si el esquema de la tabla de productos cambia, podría potencialmente romper el servicio de órdenes que depende de esa tabla para su funcionamiento.
    2. Falta de Flexibilidad: Con una base de datos compartida, realizar cambios o actualizaciones se vuelve riesgoso. Las modificaciones deben planificarse y ejecutarse con cuidado extremo para evitar interrupciones en otros servicios que también acceden a la misma base de datos.
    3. Escalabilidad Complicada: A medida que la aplicación crece, escalar una base de datos monolítica puede convertirse en un cuello de botella, ya que todos los servicios aumentan su carga simultáneamente, pudiendo afectar el rendimiento general.

La Solución de Microservicios con Spring

Adoptar una arquitectura de microservicios significa que cada parte de la aplicación opera de manera independiente con su propia base de datos. Esto produce múltiples ventajas:

    • Desacoplamiento: Los servicios son autónomos y no están directamente ligados a los esquemas de los demás servicios. Por ejemplo, el servicio de Pagos puede evolucionar y cambiar su base de datos sin afectar a los demás servicios como Productos o Entregas.
    • Flexibilidad en Desarrollo y Mantenimiento: Cada equipo puede desarrollar, probar y desplegar actualizaciones en su servicio de manera independiente, lo que acelera los ciclos de desarrollo y reduce los riesgos asociados con las actualizaciones en producción.
    • Optimización Específica: Cada servicio puede utilizar la tecnología de base de datos que mejor se adapte a sus necesidades específicas. Por ejemplo, un servicio de Pagos podría beneficiarse de usar una base de datos SQL para manejar transacciones complejas, mientras que un servicio de Productos podría optar por una solución NoSQL para manejar grandes volúmenes de datos con estructuras menos rígidas.

ImplementaciónPráctica de Microservicios con Spring Framework

Ejemplo Práctico de Microservicios de Orders y Productos

A continuación, presentamos un ejemplo más práctico que ilustra cómo dos microservicios, el Servicio de Pedidos y el Servicio de Productos, pueden ser implementados y comunicarse entre sí utilizando Spring Boot y Spring Cloud.

1. Creación del Servicio de Productos:

El Servicio de Productos gestionará información sobre productos disponibles para ser comprados. A continuación, se muestra una configuración básica de este servicio en Spring Boot:Comunicación entre microservicios: 

En este código, ProductService es una clase de servicio que se encargará de interactuar con la base de datos (no mostrada aquí por brevedad) para recuperar los detalles del producto.

2. Creación del Servicio de Pedidos

El Servicio de Pedidos será responsable de manejar la creación de pedidos que incluyen productos:

En este código, el OrderController utiliza RestTemplate para hacer llamadas HTTP al Servicio de Productos para validar los productos antes de crear un pedido. La URL http://product-service/products/ asume que estamos utilizando algún tipo de descubrimiento de servicios, como Eureka, para resolver el servicio real a partir de su nombre lógico.

Código del Ejemplo

Comunicación entre microservicios

El otro gran desafío que tenemos que resolver es la comunicación entre servicios. Por lo tanto, dividimos nuestra aplicación en diferentes servicios pequeños. Pero ya no podemos llamar a funciones entre dos servicios como lo hacíamos en un monolito. En otras palabras, ¿cómo obtiene el servicio de Órdenes los pedidos para un informe? Hay dos formas de resolver ese problema (de hecho, hay más de dos soluciones, pero nos centraremos en estas dos). Spring Framework ofrece varias opciones para facilitar esta comunicación, adaptándose a las necesidades de sincronización o asincronización según el caso de uso.

Comunicación sincrónica

Esta es la forma más sencilla de entender e implementar, por lo que empezaremos por aquí. Utilizamos un modelo de solicitud-respuesta para comunicarnos entre servicios. Aunque no nos limitamos exclusivamente a HTTP, este es el método más común (otras soluciones pueden ser RPC y protocol buffers).

Permíteme mostrarte un diagrama actualizado:

diagrama de comunicación sincrónica

Cada flecha en el diagrama representa una solicitud API HTTP. Así es cómo funciona en orden:

    1. El navegador envía una solicitud HTTP al servicio de Order y al servicio de Product.
    2. Tanto Order como Product envían solicitudes al servicio de Payment y al servicio de Delivery.
    3. Payment y Delivery procesan estas solicitudes y devuelven la respuesta necesaria.

Características de la Comunicación Sincrónica

Esto se conoce como comunicación sincrónica porque cada paso se espera a que el anterior haya completado:

    • Los servicios de Order y Product, tras recibir la solicitud inicial, deben completar sus propias operaciones y luego comunicarse con Payment y Delivery para proceder.
    • Después de que cada solicitud es procesada, Payment y Delivery devuelven los resultados necesarios para que Order y Product puedan finalizar sus procesos y responder al requerimiento inicial del Actor.

Ventajas y Desafíos de la Comunicación Sincrónica:

Ventajas:

    • Claridad en el proceso: Es fácil de entender, muy parecido a las llamadas de funciones en una arquitectura monolítica, aunque en forma de solicitudes HTTP.
    • Desacoplamiento de los servicios: Aunque los servicios se están comunicando, cada uno mantiene su independencia, facilitando su desarrollo y mantenimiento individual.

Desafíos:

    1. Dependencia entre servicios. Esto es importante. Queremos usar microservicios porque podemos desarrollar servicios independientemente unos de otros, y podemos desplegarlos cuando queramos. Si los acoplamos con llamadas HTTP, solo complica las cosas.
    2. Más fuentes de errores. Hacemos muchas solicitudes HTTP dentro de nuestros servicios. ¿Qué pasa si la solicitud de Order a Product falla? Entonces toda la cadena de solicitudes falla. ¿Qué pasa si un servicio está caído y ocurre un tiempo de espera? Entonces toda la cadena de solicitudes falla. Esto nos lleva a nuestra próxima desventaja.
    3. Latencia. Hacemos muchas solicitudes. Imagina que tenemos solo 30 servicios. Podemos fácilmente hacer una cadena de solicitudes de 20-30 llamadas HTTP. Es simplemente lento, mucho más lento que 20-30 llamadas a funciones en un monolito.
    4. Dependencias circulares. ¿Qué pasa si el servicio de Order llama al servicio de Product, y luego el servicio de Product llama al servicio de Delivery, pero por alguna razón, el servicio de Delivery también llama al servicio de Product?

Llamada del servicio order al servicio product

Consideraciones Finales sobre la Comunicación Sincrónica

Aunque la comunicación sincrónica es directa y a menudo más fácil de implementar, presenta desafíos que pueden complicar la arquitectura de microservicios. Es vital considerar estos factores al diseñar sistemas que dependen de la inmediatez de la respuesta.

¿Entonces, qué otra alternativa tenemos?

Comunicación Basada en Eventos Utilizando Kafka

Introducción al Modelo de Eventos con Kafka

Kafka actúa como una plataforma de streaming distribuido. Kafka permite a los servicios publicar y suscribirse de flujos de eventos de manera eficiente y con alta disponibilidad, facilitando así la comunicación asincrónica entre los servicios.

Este modelo permite una comunicación asincrónica entre los servicios, mejorando la desacoplación y la escalabilidad horizontal.

Diagrama Ejemplo Práctico: Uso de Kafka en la creación de Órdenes

A continuación, se muestra cómo los servicios pueden integrar Kafka para manejar eventos de manera efectiva.

Microservicios con Spring. Diagrama de uso de kafka

Creación de la Orden

  1. Inicio del Proceso
      • Un actor (por ejemplo, un usuario) envía una solicitud para crear una nueva orden mediante un POST /orders.
      • El servicio de Order recibe esta solicitud HTTP POST con los datos de la orden.
  2. Procesamiento y Almacenamiento
    • El servicio de Order procesa la solicitud, calcula lo necesario, y guarda los detalles de la nueva orden en su base de datos Orders DB.
  3. Publicación del Evento
    • Una vez creada la orden, el servicio de Order publica un evento llamado OrderCreated en un tema específico de Kafka denominado Kafka Topic: Orders. Este evento incluye toda la información relevante sobre la orden, como el monto total, la mesa, el cliente, y el arreglo de productos.
  4. Distribución del Evento
    • Kafka, actuando como intermediario, distribuye el evento OrderCreated a todos los servicios suscritos que estén interesados en este tipo de eventos.
  5. Consumo y Procesamiento del Evento
    • Los servicios de Payment y Delivery están configurados para consumir eventos de este tipo desde el tema de Kafka.
    • Al recibir el evento, ambos servicios procesan la información relevante y realizan operaciones de inserción o actualización en sus respectivas bases de datos (Payment db y Delivery db).

Resultados y Beneficios:

    • Este modelo permite que cada servicio construya y mantenga su propia base de datos basada en los eventos que consume, lo que minimiza la necesidad de solicitar información directamente de otros servicios.
    • Por ejemplo, no es necesario que el servicio de Payment realice una solicitud HTTP directamente al servicio de Order para obtener los datos que necesita.

Nota Importante sobre el Uso de Kafka:

    • Utilizando Kafka como sistema de manejo de eventos, se mejora la eficiencia en la comunicación entre servicios al reducir la dependencia directa y al permitir un sistema más escalable y resiliente.

kafka con manejo de eventos

Flujo de Datos en el Servicio de Órdenes Utilizando Kafka

Creación de la Orden

  • Inicio de la solicitud:

    • Un actor, como un usuario o un sistema automatizado envía una solicitud POST al endpoint /orders para crear una nueva orden.
  • Procesamiento del Evento

    • El servicio de Ordenes procesa esta solicitud y guarda los detalles de la orden en la base de datos, específicamente en la tabla de órdenes. Este detalle almacenado es lo que llamamos el modelo interno de Orden, que solo es visible para el servicio de Ordenes y puede contener hasta 20 columnas.

Publicación del Evento

  • Generación del Evento OrderCreated

    • Una vez que la orden está completamente procesada y almacenada, el servicio de Órdenes publica un evento denominado OrderCreated en el tópico de Kafka llamado “Orders”. Este evento incluye detalles esenciales pero simplificados de la orden, conocidos como el “modelo compartido de la orden” o DTO. Esta representación contiene solo información crítica necesaria para otros servicios, garantizando que los detalles internos permanezcan privados y seguros.
  • Distribución y Consumo del Evento

    • Distribución Automática del Evento:
      • Kafka distribuye el evento OrderCreated a todos los servicios suscritos que están configurados para escuchar este tópico específico.
    • Procesamiento del Evento por Servicios Subscriptores:
      • Los servicios como Payment y Delivery consumen el evento. Utilizan la información del modelo compartido de la orden para actualizar sus propias bases de datos y realizar operaciones necesarias sin requerir una comunicación directa con el servicio de Órdenes.

Consideraciones Importantes

  • Seguridad y Privacidad de la Información:

    • El evento publicado por el servicio de Órdenes incluye solo el modelo compartido de la orden (DTO), asegurando que la información detallada y sensible no trascienda los límites del servicio que la genera. Esto mantiene la integridad y la confidencialidad de los datos internos.

La promesa de los microservicios

Espero que todo esto suene bien y muy arquitectónico, pero hay una pregunta más importante. ¿Por qué estamos haciendo esto?

Ventajas de los Microservicios

1. Modularidad y Escalabilidad

    • Base de Código Reducida: Todos nuestros servicios serán pequeños, fáciles de entender y simples. Esto permite una mayor manejabilidad y menor complejidad.
    • Resiliencia y Fiabilidad: Casi todos los errores y bugs tienen un alcance de un solo servicio. Esto significa que si algo sale mal en un servicio, no afecta a todo el sistema.
    • Despliegues Frecuentes: Con servicios más pequeños, es más fácil desplegar y lanzar nuevas características frecuentemente sin riesgos mayores.
    • Disponibilidad Mejorada: Utilizando herramientas como Docker Swarm o Kubernetes para orquestar los servicios, se mejora la disponibilidad y se facilita la escalabilidad.

2. Eficiencia de Desarrollo

    • Equipos Pequeños y Focalizados: Equipos de 4-5 miembros pueden desarrollar y mantener unos pocos servicios, facilitando la gestión del desarrollo y reduciendo la curva de aprendizaje.
    • Flexibilidad Tecnológica: Se puede utilizar una variedad de lenguajes y tecnologías en diferentes servicios, permitiendo a los equipos elegir las herramientas más adecuadas para sus necesidades específicas.

3. Optimización del Ciclo de Vida del Software

    • Equipos Autónomos: Cada equipo puede manejar todo el ciclo de vida de una característica o servicio, desde la idea hasta el mantenimiento.
    • Pipelines de Integración y Despliegue Rápidos: Los servicios más pequeños permiten pruebas y construcciones más rápidas, agilizando los ciclos de CI/CD.

Desventajas de los Microservicios

1. Complejidad Operativa

    • Dificultad de Entendimiento: La interacción entre múltiples servicios puede ser complicada y difícil de entender para nuevos desarrolladores.
    • Retos en la Depuración: La depuración puede ser más complicada cuando los errores atraviesan varios servicios, especialmente en comunicaciones asincrónicas como las impulsadas por eventos.
    • Monitoreo y Registro de Datos: Monitorear y registrar adecuadamente una aplicación distribuida requiere herramientas especializadas y puede ser intrincadamente complejo.

2. Desafíos Técnicos

    • Problemas de Datos Distribuidos: La consistencia de los datos puede ser un desafío, especialmente cuando los eventos se procesan en un orden no deseado, llevando a inconsistencias.
    • Modificaciones en Múltiples Servicios: Actualizaciones que requieren cambios en varios servicios pueden ser tediosas y propensas a errores.

Conclusión

El Spring Framework proporciona un conjunto robusto de herramientas que facilitan la implementación de microservicios, ayudando a las empresas a construir sistemas más flexibles y escalables. A pesar de los desafíos, los beneficios a largo plazo de adoptar microservicios justifican su uso en entornos que requieren alta disponibilidad y adaptabilidad.

Referencias 

Nosotros y terceros seleccionados utilizamos cookies o tecnologías similares con fines técnicos y, con su consentimiento, para otras finalidades (“interacciones y funcionalidades básicas”, “mejora de la experiencia”, “medición” y “segmentación y publicidad”) según se especifica en la política de cookies. Usted es libre de otorgar, denegar o revocar su consentimiento en cualquier momento.    Configurar y más información
Privacidad