JavaRush /Курсы /Модуль 5. Spring /Лекция 206: Практика: проектирование событийно-ориентиров...

Лекция 206: Практика: проектирование событийно-ориентированной архитектуры

Модуль 5. Spring
21 уровень , 5 лекция
Открыта

В прошлых лекциях мы познакомились с Event-Driven Architecture (EDA) — событийно-ориентированной архитектурой, её принципами и компонентами. Мы разобрали, как события создают слабую связанность между микросервисами, снижают зависимость и улучшают масштабируемость. Вы узнали, как работают асинхронные коммуникации, изучили концепции издателей (producers) и подписчиков (subscribers), а также поняли, как брокеры сообщений, такие как Kafka, RabbitMQ и ActiveMQ, управляют передачей данных. Также мы обсудили типичные ошибки при внедрении EDA и лучшие практики её проектирования.

Пришло время применить теорию на практике и попробовать спроектировать событийно-ориентированную архитектуру для конкретного кейса.


Практическое проектирование событийно-ориентированной архитектуры

Постановка задачи: кейс "Магазин электронной коммерции"

Представьте, что вам нужно разработать архитектуру событий для интернет-магазина. Как главный архитектор системы, вы сталкиваетесь с такими задачами:

  • Обработка заказов, включая уведомления о статусе заказов, оплату, и доставку.
  • Автоматическое обновление информации о наличии товаров на складе.
  • Отправка уведомлений пользователям о скидках на их любимые товары.
  • Логирование всех событий для аналитики (например, для построения отчетов).

Наша цель — использовать событийно-ориентированный подход, чтобы снизить связанность сервисов и повысить отказоустойчивость.


Шаг 1: Определение событий

Первый шаг — понять, какие события происходят в системе. Событие — это некая "новость" о том, что что-то случилось. Например:

Событие Описание
OrderPlaced Заказ был создан пользователем
OrderPaid Пользователь оплатил заказ
OrderShipped Заказ отправлен службой доставки
InventoryUpdated Изменилось количество товара на складе
DiscountCreated Создана новая скидка, которая может заинтересовать пользователей
UserNotificationSent Уведомление отправлено пользователю

Каждое событие имеет свою полезную нагрузку (payload) и метаданные, такие как время создания, идентификатор пользователя и т.д.

Шаг 2: Идентификация издателей и подписчиков

Теперь, когда мы определили события, нужно понять, кто их производит (издатели) и кто на них реагирует (подписчики).

Событие Издатель Подписчики
OrderPlaced Order Service Inventory Service, Payment Service
OrderPaid Payment Service Shipping Service, Notification Service
OrderShipped Shipping Service Notification Service
InventoryUpdated Inventory Service Analytics Service, Notification Service
DiscountCreated Admin Service Notification Service
UserNotificationSent Notification Service Analytics Service

Пример:

  • Когда пользователь создаёт заказ, Order Service публикует событие OrderPlaced. Это событие обрабатывается Inventory Service, чтобы уменьшить количество товаров на складе, и Payment Service, чтобы инициировать процесс оплаты.

Шаг 3: Создание схемы взаимодействия

Давайте визуализируем, как услуги обмениваются событиями. Вот схема, показывающая взаимодействие через брокер сообщений (например, Apache Kafka):

User
  ↓
Order Service ──> OrderPlaced ─┬──> Inventory Service (уменьшить склад)
                               └──> Payment Service (инициировать оплату)
                                    ↓
                                    Payment Service ──> OrderPaid ─┬──> Shipping Service (отправить заказ)
                                                                   └──> Notification Service (уведомить пользователя)
                                    ↓
Shipping Service ──> OrderShipped → Notification Service

Шаг 4: Определение структуры событий

События должны содержать полезную информацию для подписчиков.

Пример структуры события OrderPlaced:

{
  "eventId": "12345",
  "eventType": "OrderPlaced",
  "timestamp": "2023-10-01T12:34:56Z",
  "payload": {
    "orderId": "7890",
    "userId": "456",
    "orderItems": [
      {
        "productId": "101",
        "quantity": 2
      },
      {
        "productId": "202",
        "quantity": 1
      }
    ],
    "totalPrice": 159.99
  }
}

Подобные структуры помогут унифицировать обработку событий, ведь подписчики всегда знают, чего ожидать.

Шаг 5: Моделирование брокера сообщений

Для реализации архитектуры мы будем использовать Apache Kafka. Каждое событие будет публиковаться в свой топик.

Топик Тип события Примеры сообщений
orders OrderPlaced Данные о созданных заказах
payments OrderPaid Данные об успешных оплатах
shipping OrderShipped Данные об отправленных заказах
inventory InventoryUpdated Данные об изменении остатков на складе
notifications UserNotificationSent Данные об отправленных уведомлениях

Упражнение: реализация продюсера и консьюмера для события OrderPlaced

Реализация продюсера:


// Producer для отправки события OrderPlaced в Kafka
@Component
public class OrderEventProducer {

    private final KafkaTemplate<String, String> kafkaTemplate;

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

    public void sendOrderPlacedEvent(String orderId, String eventPayload) {
        kafkaTemplate.send("orders", orderId, eventPayload);
        System.out.println("Событие OrderPlaced отправлено: " + eventPayload);
    }
}

Реализация подписчика:


// Consumer для обработки события OrderPlaced
@Component
@KafkaListener(topics = "orders", groupId = "inventory-service")
public class OrderEventConsumer {

    @Autowired
    private InventoryService inventoryService;

    @KafkaHandler
    public void handleOrderPlacedEvent(String message) {
        System.out.println("Получено событие OrderPlaced: " + message);
        inventoryService.updateInventory(message);
    }
}

Советы по проектированию событийно-ориентированной архитектуры

  • Минимизируйте зависимости. Каждый сервис должен быть максимально изолирован. Например, Notification Service не должен знать внутреннюю логику Order Service.
  • Логируйте события. Это поможет отследить, что происходит в системе, особенно в асинхронной обработке.
  • Идентифицируйте схемы событий. Используйте JSON Schema, чтобы все события следовали чёткому формату.
  • Обеспечьте согласованность. Если событие не обработано, его можно повторить благодаря Kafka's "At least once".

Заключительное упражнение

Для закрепления знаний спроектируйте событийно-ориентированную архитектуру для системы бронирования авиабилетов. Определите ключевые события (FlightBooked, PaymentProcessed, TicketIssued), их издателей и подписчиков. Опишите взаимодействие сервисов и создайте схему событий.

Вот и всё! Теперь вы практическим путём освоили основы проектирования событийно-ориентированной архитектуры. Впереди — ещё больше EDA и асинхронной магии.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ