CodeGym /Cursos /Módulo 5. Spring /Lección 169: Cómo descomponer correctamente las aplicacio...

Lección 169: Cómo descomponer correctamente las aplicaciones: zonas de responsabilidad de los microservicios

Módulo 5. Spring
Nivel 11 , Lección 8
Disponible

La descomposición es el proceso de dividir un sistema grande (por ejemplo, un monolito) en componentes más pequeños e independientes (en nuestro caso — microservicios). Estos componentes deben ser autónomos, manejables y mantenibles por separado. Pero no todo es tan sencillo: dividir una aplicación en microservicios no es lo mismo que cortar un pastel en porciones iguales. Aquí no se puede improvisar: es importante tener en cuenta la lógica de negocio, los límites de contexto y minimizar las dependencias entre servicios.

Si alguna vez intentaste montar un mueble de IKEA sin instrucciones, más o menos sabes el tipo de caos que puede surgir con una mala descomposición. Tenemos que asegurarnos de que cada parte del sistema tenga una zona de responsabilidad clara y que la interacción con otros componentes sea mínima, para no convertirnos en un "monolito distribuido".


Principios de la descomposición

  1. Separación por funciones de negocio, no por componentes técnicos. Uno de los antipatrónes más comunes al pasarse a microservicios es intentar descomponer la aplicación por capas (UI, business logic, base de datos). Eso multiplica las dependencias entre servicios y crea problemas adicionales al desplegarlos de forma independiente. Por ejemplo, en lugar de crear un servicio para todos los datos relacionados con clientes y otro para todos los datos de pedidos, es mejor crear servicios separados para gestionar clientes (Customer Service) y pedidos (Order Service). Cada uno manejará sus propios datos de forma autónoma y la interacción entre servicios se reducirá al mínimo.
  2. Evita microservicios "pesados" (fat). A veces da la tentación de volcar toda la lógica relacionada en un solo microservicio. Pero ese servicio se vuelve difícil de escalar y mantener, convirtiéndose en un monolito dentro de la arquitectura de microservicios. Es como una maleta sin asa: ni puedes llevarla bien, ni quieres dejarla tirada.
  3. Baja acoplamiento (Loose Coupling). Los microservicios deben ser lo más independientes posible. Cualquier interacción entre ellos debe minimizarse y formalizarse a través de interfaces, como APIs o eventos. Esto facilita las pruebas, las actualizaciones y el escalado de componentes individuales.
  4. Alta cohesión (High Cohesion). El código dentro de un microservicio debe estar unido por objetivos comunes. Es como con los colegas: con cierto grupo mola ir al cine, y con otro — hablar de microservicios. Pero mezclarlos no suele funcionar; alguno siempre se queda fuera de onda.

Definición de los límites de los microservicios

El enfoque más popular para la descomposición de microservicios es usar el concepto Bounded Context, propuesto en el marco de Domain-Driven Design (DDD). Cada microservicio debe atender una área de negocio concreta (o un subconjunto de ella), ofreciendo mecanismos de interacción bien definidos con otros servicios.

Ejemplo:

  • Clientes (Customer Service)
  • Pedidos (Order Service)
  • Pagos (Payment Service)
  • Notificaciones (Notification Service)

Así podría estar organizado, por ejemplo, una tienda online. En lugar de un monolito que lo hace todo (gestiona clientes, pedidos, pagos, notificaciones), creamos microservicios separados, cada uno responsable de una necesidad de negocio concreta.


¿Cómo evitar antipatrónes comunes?

  1. Descomposición anárquica. Cuando no hay estrategia, los desarrolladores crean microservicios "como salgan". El problema es que esto conduce a monolitos distribuidos, con sistemas demasiado acoplados y dificultades para escalar.
  2. Duplicación de datos. La duplicación de datos entre servicios es a veces inevitable, pero hay que minimizarla. Por ejemplo, el servicio de pedidos no debe almacenar los datos completos del cliente; basta con el ID del cliente para pedir la información al Customer Service.
  3. Excesiva granularidad. No crees microservicios demasiado pequeños. Si intentas hacer un servicio por cada acción (por ejemplo, un servicio separado para "añadir producto al carrito"), acabarás con un sobrecoste enorme en comunicación.

Ejercicio práctico: descomposición de una tienda online

Supongamos que estáis desarrollando una tienda online grande. ¿Cómo dividirla en microservicios?

1. Definimos las áreas principales (Business Domains):

  • Gestión de clientes (Customer Management)
  • Gestión del catálogo de productos (Product Catalog)
  • Gestión de pedidos (Order Management)
  • Procesamiento de pagos (Payment Processing)
  • Gestión de notificaciones (Notifications)

2. Separamos los datos: cada microservicio debe ser propietario de sus datos:

  • Customer Service almacena información de clientes (nombre, email, historial de pedidos, etc.).
  • Product Catalog Service gestiona la información de productos (nombre, descripción, precio, stock).
  • Order Service procesa pedidos (detalles del pedido, estados).
  • Payment Service se encarga del procesamiento de transacciones (pagos, reembolsos).
  • Notification Service envía notificaciones (email, SMS).

3. Definimos las interacciones:

  • Customer Service transmite información de clientes a Order Service.
  • Order Service solicita datos de productos a Product Catalog Service.
  • Order Service envía solicitudes de pago a Payment Service.
  • Payment Service comunica pagos exitosos a Order Service.
  • Todos los eventos pueden desencadenar notificaciones a través de Notification Service.

4. Elegimos los enfoques de interacción

  • Para interacción síncrona usamos REST API. Para asíncrona — por ejemplo, Kafka: eventos como "nuevo pedido creado" o "pago realizado con éxito" pueden publicarse en Kafka para que otros microservicios los procesen.

Pasos para descomponer un monolito en microservicios

  1. Análisis del sistema actual
    • Intenta identificar las funciones de negocio principales de tu monolito.
  2. Define las zonas de responsabilidad
    • Agrupa módulos funcionales relacionados en una única área de responsabilidad.
  3. Separa las bases de datos
    • Distribuye los datos entre servicios para que cada servicio gestione su propia base de datos.
  4. Asegura la interacción
    • Elige entre interacción síncrona (REST) o asíncrona (Kafka).
  5. Migración de funcionalidad
    • Migra gradualmente la funcionalidad del monolito a los microservicios.
  6. Testing
    • Asegúrate de que los nuevos módulos funcionan correctamente y se integran entre sí.

Ejemplo: REST API entre servicios

Para la interacción entre Order Service y Product Catalog Service:


// Cliente REST de Order Service para obtener datos de productos
@RestController
@RequestMapping("/order")
public class OrderController {

    private final RestTemplate restTemplate;

    public OrderController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @PostMapping
    public ResponseEntity<String> createOrder(@RequestBody OrderRequest orderRequest) {
        // Envío de la solicitud al Product Catalog Service
        ResponseEntity<Product> productResponse = restTemplate.getForEntity(
            "http://product-service/product/" + orderRequest.getProductId(),
            Product.class
        );

        if (productResponse.getStatusCode().is2xxSuccessful()) {
            // Procesamiento del pedido
            return ResponseEntity.ok("¡Pedido creado con éxito!");
        } else {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Producto no encontrado");
        }
    }
}

Para el envío asíncrono de eventos usa Kafka:


// Publicación del evento "Order Created" en Kafka
@Service
public class OrderEventPublisher {

    private final KafkaTemplate<String, String> kafkaTemplate;

    public OrderEventPublisher(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    public void publishOrderCreatedEvent(Order order) {
        kafkaTemplate.send("orders", order.toString());
    }
}

Conclusiones

Una descomposición correcta requiere un entendimiento profundo de tus procesos de negocio, del funcionamiento del sistema y de los posibles cuellos de botella. Un servicio debe ser lo suficientemente grande como para cumplir una tarea con sentido, pero lo bastante sencillo para desarrollar, testear y desplegar. Ten siempre en mente el principio principal: un microservicio — una función de negocio.

Al igual que en la cocina de un restaurante, donde cada cocinero se encarga de su estación, los microservicios ejecutan sus responsabilidades de forma independiente, pero juntos forman un sistema coherente.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION