JavaRush /Курси /Модуль 5. Spring /Лекція 215: Практика: реалізація патерна Saga для розподі...

Лекція 215: Практика: реалізація патерна Saga для розподіленого процесу

Модуль 5. Spring
Рівень 14 , Лекція 4
Відкрита

Ми розглянули два основні підходи до координації Saga: оркестрація, коли є централізований координатор, і хореографія, коли мікросервіси обмінюються подіями напряму — як на вечірці, де всі танцюють без діджея. В цьому практичному занятті ми зануримось у реалізацію патерна Saga на практиці.

Бізнес-кейс: бронювання подорожі

Припустимо, ми робимо застосунок для туристичного агентства. Користувач хоче забронювати пакет "усе включено": переліт, готель і екскурсію. Однак кожен з цих кроків виконується через окремі мікросервіси:

  • Flight Service — відповідає за бронювання авіаквитків.
  • Hotel Service — обробляє бронювання номерів у готелі.
  • Tour Service — керує замовленням екскурсій.

Наша задача — реалізувати процес бронювання через патерн Saga, щоб:

  1. У випадку успіху всіх кроків ми підтверджували бронювання.
  2. У разі невдачі на будь-якому етапі ми відкатували попередні кроки (скасовували бронь квитків, звільняли номери в готелі тощо).

Проєктування системи

Перед тим як поринути в код, давай розберемося, як виглядатиме процес на рівні взаємодії мікросервісів. Ми оберемо підхід оркестрації, щоб простіше координувати процес.

Кроки Saga

  1. Розпочати бронювання.
  2. Забронювати авіаквитки в Flight Service.
  3. Забронювати готель в Hotel Service.
  4. Забронювати екскурсію в Tour Service.
  5. Завершити бронювання при успіху.
  6. Скасувати всі раніше виконані дії у випадку помилки.

Діаграма взаємодії


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

Компенсаційні дії

Кожна дія в Saga має супроводжуватися можливістю відкату. Наприклад, якщо бронювання авіаквитків пройшло успішно, а бронювання готелю впало, нам потрібно скасувати бронь квитків.


Реалізація

Ми будемо використовувати Spring Boot і Spring Kafka для реалізації саги з оркестратором. Для простоти залишимо наші мікросервіси як емулятори (наприклад, прості REST-контролери).

Підготовка проєкту

Створимо три мікросервіси: Orchestrator, FlightService, HotelService, TourService.

Крок 1: Налаштування Orchestrator

Почнемо зі створення сервісу Orchestrator для управління сагою.

Додаємо залежності в 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>

Створюємо структуру проєкту:

.
├── 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.";
        }
    }
}

Крок 2: Створення сервісів FlightService, HotelService, TourService

FlightServiceClient.java


@Component
public class FlightServiceClient {

    public String reserveFlight() {
        // Емуляція бронювання
        System.out.println("Reserving flight...");
        return UUID.randomUUID().toString(); // повертаємо фіктивний ID
    }

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

Аналогічно створіть HotelServiceClient та TourServiceClient.


Додавання Kafka для передачі подій

Тепер, щоб наші мікросервіси спілкувалися між собою, давай інтегруємо Kafka. Наприклад, FlightService буде відправляти події про підтвердження броні, а Orchestrator їх підхоплюватиме.

Налаштування 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: обробка відповідей:

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

Ми побудували основи Saga з простим управлінням через мікросервіси. Наступні кроки включають додавання детальної обробки помилок і покращення логування через розподілені трасування.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ