JavaRush /Курси /Модуль 5. Spring /Лекція 204: Коли використовувати події: підходи й кращі п...

Лекція 204: Коли використовувати події: підходи й кращі практики

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

Ми вже почали розбиратися, що таке події. Якщо ти відправив повідомлення другові, і він не відповідає одразу, зазвичай ти просто чекаєш відповіді — через події, а не постійно «опитуєш» месенджер. Звісно, якщо це не щось дуже термінове. Події в програмуванні працюють схожим чином.

Вони дозволяють системі працювати асинхронно й відокремлено. Наприклад:

  • Один сервіс може повідомити інший сервіс про зміну стану.
  • Система може будувати ланцюжки дій, де одна подія ініціює наступну.

Коли використовувати події в EDA?

Події ідеально підходять для задач, де:

  1. Слабка зв'язаність компонентів критична. Це дозволяє системі бути стійкою й модульною.
  2. Асинхронна обробка даних переважніша за синхронний виклик API.
  3. Потрібно працювати з великим об'ємом даних, які треба обробляти крок за кроком. Наприклад, обробка замовлень в e-commerce або оновлення стану системи.
  4. Потреба в scalability (масштабованості). Події допомагають розподіляти навантаження між мікросервісами.
  5. Хочеш, щоб твої компоненти працювали незалежно, навіть якщо один з них тимчасово недоступний.

Як застосовувати події? Практичні приклади

Сповіщення та події

Припустимо, у тебе є e-commerce додаток. Коли користувач оформлює замовлення, ця дія може запустити кілька процесів:

  • Відправка сповіщення про замовлення.
  • Списання товару зі складу.
  • Пакування й доставка.

Тут подія "Замовлення оформлене" виступає тригером. Компоненти взаємодіють через події, а не напряму, що робить їх незалежними.

Приклад:


// Подія, що ініціюється при оформленні замовлення
public class OrderCreatedEvent {
    private final String orderId;
    private final LocalDateTime timestamp;

    public OrderCreatedEvent(String orderId) {
        this.orderId = orderId;
        this.timestamp = LocalDateTime.now();
    }

    public String getOrderId() {
        return orderId;
    }

    public LocalDateTime getTimestamp() {
        return timestamp;
    }
}


// Продюсер: створює подію й відправляє її
@Component
public class OrderEventProducer {

    private final KafkaTemplate<String, OrderCreatedEvent> kafkaTemplate;

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

    public void publishOrderEvent(OrderCreatedEvent event) {
        kafkaTemplate.send("orders-topic", event);
        System.out.println("Order event sent: " + event.getOrderId());
    }
}


// Підписник: обробляє подію
@Component
@KafkaListener(topics = "orders-topic", groupId = "order-group")
public class NotificationService {

    @KafkaHandler
    public void handleOrderEvent(OrderCreatedEvent event) {
        System.out.println("Sending notification for order: " + event.getOrderId());
    }
}

Асинхронне оновлення стану

Припустимо, ти створюєш додаток для відстеження доставки. Служби доставки працюють незалежно, але оновлюють статус у центральній системі.

  1. Кожна служба відправляє подію "Доставка оновлена".
  2. Централізований сервіс обробляє події й оновлює базу даних.

Такий підхід зменшує навантаження на центральний сервіс і пришвидшує обробку.


Шаблони проєктування для подій

Шаблон "Best-Effort Messaging". Цей підхід не гарантує доставку повідомлення, але підходить для задач, де втрата повідомлення не критична. Наприклад, логування подій.

Шаблон "Guaranteed Delivery". Якщо подія має бути доставлена обов'язково, використовуйте бібліотеки або брокери повідомлень, які гарантують доставку принаймні один раз ("At Least Once"). Наприклад, Kafka успішно справляється з цим завданням, використовуючи реплікацію повідомлень.

Шаблон "Dead Letter Queue (DLQ)". Що робити, якщо повідомлення не може бути оброблене? Можна відправити його в DLQ — чергу для "проблемних" повідомлень. Це дозволяє відслідковувати помилки й виправляти їх.


Підходи до оптимізації подійного підходу

* Використання подій з мінімальним навантаженням*

Подія має нести лише необхідну інформацію. Наприклад, подія "Замовлення створене" не повинна містити 10 МБ даних про замовлення. Достатньо ідентифікатора замовлення.

Поганий приклад:

{
    "orderId": "12345",
    "customerData": { "name": "Іван Іванов", "email": "ivanov@example.com" },
    "products": [
        { "id": "prod1", "name": "Телефон", "quantity": 1 },
        { "id": "prod2", "name": "Чохол", "quantity": 2 },
        ...
    ]
}

Кращий варіант:

{
    "orderId": "12345",
    "timestamp": "2023-10-20T12:00:00"
}

Ієрархія подій

Події часто мають родинні зв'язки. Наприклад:

  • Подія "Платіж оброблено" може породити "Платіж підтверджено" або "Платіж відхилено".
  • Правильна структуризація подій спростить їхню обробку.

Поради й практичні рекомендації

  1. Не роби занадто складну логіку для обробки подій. Простота — запорука успіху.
  2. Дотримуйся формату подій. JSON або Avro допоможуть стандартизувати повідомлення.
  3. Регулярно моніторь події. Використовуй такі інструменти, як Grafana або ELK Stack, щоб відслідковувати, що відбувається з твоїми подіями.
  4. Подумай про продуктивність брокера повідомлень. Наприклад, Kafka підтримує тисячі подій на секунду — це добре підходить для високонавантажених систем.
  5. Документуй події. Опиши, що означає кожна подія, щоб полегшити роботу іншим розробникам.

Коли не варто використовувати події?

EDA — це не срібна куля. Якщо твоя система вимагає суворої послідовності виконання й синхронності, наприклад при обробці банківських транзакцій, події можуть додати зайвої складності. Для таких задач краще підходять синхронні виклики (REST, gRPC).


Отже, використання подій допомагає будувати високопродуктивні, гнучкі та масштабовані системи. Важливо пам'ятати, що події — це інструмент, і застосовувати його варто розумно. У наступній лекції ми почнемо розбирати інструменти, які допоможуть ефективно працювати з EDA, включаючи Kafka, RabbitMQ і ActiveMQ.

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