CodeGym /Cursos /Módulo 5. Spring /Lección 215: Práctica: implementación del patrón Saga par...

Lección 215: Práctica: implementación del patrón Saga para un proceso distribuido

Módulo 5. Spring
Nivel 14 , Lección 4
Disponible

Hemos visto dos enfoques principales para coordinar una Saga: orquestación, cuando existe un coordinador centralizado, y coreografía, cuando los microservicios intercambian eventos directamente — como en una fiesta donde todos bailan sin DJ. En esta práctica nos meteremos en la implementación del patrón Saga en la práctica.

Caso de negocio: reserva de viaje

Supongamos que desarrollamos una aplicación para una agencia de viajes. El usuario quiere reservar un paquete "todo incluido": vuelo, hotel y excursión. Sin embargo, cada uno de esos pasos se solicita a través de microservicios separados:

  • Flight Service — se encarga de la reserva de billetes de avión.
  • Hotel Service — gestiona la reserva de habitaciones de hotel.
  • Tour Service — gestiona las reservas de excursiones.

Nuestra tarea — implementar el proceso de reserva usando el patrón Saga, de modo que:

  1. Si todos los pasos tienen éxito, se confirme la reserva.
  2. Si hay fallo en cualquier etapa, revertimos los pasos previos (cancelar reservas de billetes, liberar habitaciones de hotel, etc.).

Diseño del sistema

Antes de meternos a escribir código, veamos cómo será el proceso a nivel de interacción entre microservicios. Elegiremos el enfoque de orquestación para coordinar el proceso más fácilmente.

Pasos de la Saga

  1. Iniciar la reserva.
  2. Reservar vuelo en Flight Service.
  3. Reservar hotel en Hotel Service.
  4. Reservar excursión en Tour Service.
  5. Confirmar la reserva si todo va bien.
  6. Cancelar todas las acciones previas en caso de error.

Diagrama de interacción


Saga Orchestrator
   |
   |---> Flight Service (Reserve)
   |       |
   |       ---> Failure? --> Cancel Flight
   |
   |---> Hotel Service (Reserve)
   |       |
   |       ---> Failure? --> Cancel Hotel
   |
   |---> Tour Service (Reserve)
   |       |
   |       ---> Failure? --> Cancel Tour
   |
   ---> Booking Confirmed!

Acciones compensatorias

Cada acción en la Saga debe tener una forma de deshacerla. Por ejemplo, si la reserva del vuelo fue exitosa y la reserva del hotel falló, necesitamos cancelar la reserva del vuelo.


Implementación

Usaremos Spring Boot y Spring Kafka para implementar la Saga con un Orchestrator. Para simplificar, dejaremos nuestros microservicios como emuladores (por ejemplo, simples REST controllers).

Preparación del proyecto

Crearemos cuatro microservicios: Orchestrator, FlightService, HotelService, TourService.

Paso 1: Configurar Orchestrator

Empezamos creando el servicio Orchestrator para gobernar la Saga.

Añadimos dependencias en pom.xml:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>

Creamos la estructura del proyecto:

.
├── OrchestratorApplication.java
└── services/
    ├── FlightServiceClient.java
    ├── HotelServiceClient.java
    └── TourServiceClient.java

SagaOrchestrator.java


@Service
public class SagaOrchestrator {

    private final FlightServiceClient flightService;
    private final HotelServiceClient hotelService;
    private final TourServiceClient tourService;

    public SagaOrchestrator(FlightServiceClient flightService, HotelServiceClient hotelService, TourServiceClient tourService) {
        this.flightService = flightService;
        this.hotelService = hotelService;
        this.tourService = tourService;
    }

    public String processBooking() {
        try {
            // Step 1: book flight
            String flightBookingId = flightService.reserveFlight();
            System.out.println("Flight reserved with ID: " + flightBookingId);

            // Step 2: book hotel
            String hotelBookingId = hotelService.reserveHotel();
            System.out.println("Hotel reserved with ID: " + hotelBookingId);

            // Step 3: book tour
            String tourBookingId = tourService.reserveTour();
            System.out.println("Tour reserved with ID: " + tourBookingId);

            return "Booking successful!";
        } catch (Exception e) {
            System.err.println("Booking failed: " + e.getMessage());
            return "Booking failed and rolled back.";
        }
    }
}

Paso 2: Crear los servicios FlightService, HotelService, TourService

FlightServiceClient.java


@Component
public class FlightServiceClient {

    public String reserveFlight() {
        // Emulación de la reserva
        System.out.println("Reserving flight...");
        return UUID.randomUUID().toString(); // devolvemos un ID ficticio
    }

    public void cancelFlight(String bookingId) {
        System.out.println("Flight reservation with ID " + bookingId + " cancelled.");
    }
}

De forma análoga, crea HotelServiceClient y TourServiceClient.


Añadir Kafka para la transmisión de eventos

Ahora, para que nuestros microservicios se comuniquen entre sí, integremos Kafka. Por ejemplo, FlightService enviará eventos de confirmación de reserva, y el Orchestrator los recogerá.

Configuración de KafkaProducer:


@Service
public class KafkaProducer {

    private final KafkaTemplate<String, String> kafkaTemplate;

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

    public void sendEvent(String topic, String message) {
        kafkaTemplate.send(topic, message);
        System.out.println("Event sent to topic: " + topic + ", message: " + message);
    }
}

Orchestrator: procesamiento de respuestas:

@KafkaListener(topics = "flight-status", groupId = "orchestrator")
public void listenFlightResponses(String message) {
    System.out.println("Received flight status: " + message);
}

Hemos sentado las bases de una Saga con una gestión sencilla a través de microservicios. Los siguientes pasos incluyen añadir un manejo de errores más detallado y mejorar el registro mediante trazas distribuidas.

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