Что такое Event-Driven Architecture (EDA)
Event-Driven Architecture (EDA) — архитектурный стиль, в котором компоненты системы взаимодействуют посредством событий. Событие — факт, который произошёл в системе (например, «Платёж совершён», «Клиент зарегистрирован»). По аналогии с реальной жизнью: когда в банке совершается операция, кассир не обзванивает все отделы лично, а выписывает документ, который далее маршрутизируется по нужным адресатам.
Схема взаимодействия
Пример
┌─────────────┐ PaymentCreated ┌──────────────┐
│ Сервис │───────────────────▶│ Брокер │
│ Платежей │ │ сообщений │
└─────────────┘ │ (Kafka) │
└──────┬───────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Сервис │ │ Сервис │ │ Сервис │
│ Уведомлений│ │ Аналитики │ │ Комплаенс │
└────────────┘ └────────────┘ └────────────┘
Основные топологии EDA
1. Broker Topology (через брокер) — все события публикуются в брокер (Kafka, RabbitMQ), подписчики сами забирают нужные. Нет центрального оркестратора.
2. Mediator Topology (через посредник) — центральный компонент (медиатор) координирует обработку события, рассылая команды обработчикам.
Типы событий
- Event Notification — уведомление о факте. Содержит минимум данных. Подписчик сам запрашивает детали при необходимости.
- Event-Carried State Transfer — событие несёт в себе все необходимые данные, чтобы подписчик мог обработать его без дополнительных запросов.
- Domain Event — событие, имеющее смысл в домене бизнеса (
PaymentExecuted,AccountBlocked).
Пример на Spring с Kafka
Пример кода
// Публикация события
@Service
public class PaymentService {
private final KafkaTemplate<String, PaymentEvent> kafkaTemplate;
@Transactional
public void executePayment(PaymentCommand cmd) {
Payment payment = createAndSavePayment(cmd);
kafkaTemplate.send("payment-events",
new PaymentExecutedEvent(payment.getId(), payment.getAmount(),
LocalDateTime.now()));
}
}
// Подписчик
@Component
public class NotificationEventHandler {
@KafkaListener(topics = "payment-events", groupId = "notification-service")
public void onPaymentExecuted(PaymentExecutedEvent event) {
notificationService.sendPaymentConfirmation(event);
}
}
Плюсы
- Слабая связанность (low coupling) — продюсер не знает о подписчиках.
- Масштабируемость — можно добавлять подписчиков без изменения продюсера.
- Отказоустойчивость — сбой одного подписчика не влияет на остальных.
- Асинхронность — высокая пропускная способность.
Минусы
- Сложность отладки и трассировки.
- Eventual consistency вместо немедленной согласованности.
- Необходимость обрабатывать дублирование событий (идемпотентность).
- Сложность гарантии порядка обработки.
На собеседовании: Интервьюер хочет услышать о различии между типами событий (Notification vs Event-Carried State Transfer) и понимание eventual consistency. Частая ошибка — не упоминать проблему идемпотентности обработки событий.