← Назад к вопросам

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

1.8 Middle🔥 151 комментариев
#REST API и микросервисы#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Событийно-управляемая архитектура (Event-Driven Architecture)

Определение

Событийно-управляемая архитектура (Event-Driven Architecture, EDA) — это парадигма проектирования систем, в которой компоненты взаимодействуют друг с другом через события (события), а не через прямые вызовы (RPC, HTTP).

Вместо того чтобы компонент A напрямую вызывал метод компонента B, компонент A генерирует событие, а компонент B прослушивает это событие и реагирует на него.

Основная идея

Синхронная архитектура (плохо связана):
A → B → C → D
Если C упадёт, падёт вся цепь

Событийная архитектура (слабо связана):
A → [Event Bus] ← B
             ← C
             ← D
Если C упадёт, A и B не узнают

Ключевые компоненты

1. Источник события (Event Producer)

public class OrderService {
    private final EventPublisher publisher;
    
    public void createOrder(Order order) {
        // ... создаём заказ
        publisher.publish(new OrderCreatedEvent(order.getId()));
    }
}

2. Событие (Event)

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

3. Event Bus / Message Broker

Распределяет события между слушателями
- Apache Kafka
- RabbitMQ
- AWS SNS/SQS
- Spring Event mechanism

4. Слушатель события (Event Listener)

@Service
public class EmailNotificationListener {
    private final EmailService emailService;
    
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        emailService.sendOrderConfirmation(event.getOrderId());
    }
}

@Service
public class InventoryListener {
    private final InventoryService inventoryService;
    
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        inventoryService.reserveItems(event.getOrderId());
    }
}

Паттерны EDA

1. Publish-Subscribe (Pub-Sub)

Несколько слушателей могут слушать одно событие

OrderService publishes: OrderCreatedEvent
              ├─→ EmailService (отправит письмо)
              ├─→ InventoryService (зарезервирует товары)
              └─→ NotificationService (отправит уведомление)
// Spring Event Publisher
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public void createOrder(Order order) {
        eventPublisher.publishEvent(new OrderCreatedEvent(order));
    }
}

// Подписчик 1
@Service
public class EmailListener {
    @EventListener
    public void sendEmail(OrderCreatedEvent event) {
        // отправить письмо
    }
}

// Подписчик 2
@Service
public class InventoryListener {
    @EventListener
    public void updateInventory(OrderCreatedEvent event) {
        // обновить инвентарь
    }
}

2. Event Sourcing

Использовать события как source of truth для состояния

Вместо сохранения текущего состояния:
Order: { status: "shipped", items: [item1, item2] }

Сохраняем последовательность событий:
1. OrderCreated { order_id, items }
2. OrderPaid { order_id, amount }
3. OrderShipped { order_id, tracking_number }
// Event Store
public interface EventStore {
    void append(Event event);
    List<Event> getEvents(String aggregateId);
}

// Восстановление состояния из событий
public class Order {
    public static Order fromEvents(List<Event> events) {
        Order order = new Order();
        for (Event event : events) {
            if (event instanceof OrderCreatedEvent) {
                // применить
            } else if (event instanceof OrderPaidEvent) {
                // применить
            }
        }
        return order;
    }
}

3. CQRS (Command Query Responsibility Segregation)

Разделение операций чтения и записи

           Command (Write)          Query (Read)
              ↓                         ↓
      OrderService                 OrderView
        (creates)                   (denormalized)
           ↓                          ↑
      OrderCreatedEvent    ←────────┘
           ↓
        EventStore
// Command (Write)
@Service
public class CreateOrderCommand {
    public void execute(Order order) {
        // сохраняем в Event Store
        eventStore.append(new OrderCreatedEvent(order));
    }
}

// Query (Read)
@Service
public class OrderQueryService {
    public Order getOrder(String id) {
        // читаем из денормализованного представления
        return orderViewRepository.findById(id);
    }
}

// Update view при событии
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
    OrderView view = new OrderView(event);
    orderViewRepository.save(view);
}

Преимущества EDA

1. Слабая связанность (Loose Coupling)

Componen... могут быть независимыми и тестируемы отдельно

2. Масштабируемость (Scalability)

Можно добавлять новых слушателей без изменения producer

3. Асинхронность (Asynchronous)

Operations не блокируют друг друга

4. Resilience (Устойчивость)

Если слушатель упадёт, event всё ещё в очереди

5. Audit Trail (История)

Все события сохранены для анализа

Недостатки EDA

1. Сложность отладки (Debugging Complexity)

Что произойдёт после события? Где это обработается?
Трудно отследить поток выполнения

2. Eventual Consistency (Возможная несогласованность)

Данные не обновляются моментально
Нужно учитывать временную задержку

3. Дублирование обработки (Duplicate Handling)

Листенер может обработать событие дважды
Нужна идемпотентность

4. Testing (Тестирование)

Асинхронные операции сложнее тестировать

Реальный пример: E-commerce система

// Domain Event
public class OrderPlacedEvent {
    private String orderId;
    private List<Item> items;
    private LocalDateTime timestamp;
}

// Command Handler (создание заказа)
@Service
public class OrderCommandHandler {
    @Autowired
    private EventPublisher publisher;
    
    public void placeOrder(PlaceOrderCommand cmd) {
        Order order = Order.create(cmd);
        publisher.publish(new OrderPlacedEvent(order));
    }
}

// Event Handlers (реакции на событие)
@Service
public class OrderProcessing {
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        // Зарезервировать товары
        inventoryService.reserve(event.getItems());
    }
}

@Service
public class PaymentProcessing {
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        // Инициировать платёж
        paymentService.charge(event.getOrderId());
    }
}

@Service
public class NotificationService {
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        // Отправить уведомление
        emailService.sendOrderConfirmation(event.getOrderId());
    }
}

Kafka vs RabbitMQ vs Spring Events

Spring Events:
├─ Простая in-process коммуникация
├─ Для monolithic приложений
└─ Без persitence

RabbitMQ:
├─ Message Broker
├─ Reliable delivery
├─ Для микросервисов
└─ Требует отдельного сервера

Kafka:
├─ Event Streaming Platform
├─ High-throughput
├─ Event Sourcing ready
└─ Persistence included

Best Practices

  1. Идемпотентность — обработать дважды = один результат
  2. Версионирование событий — события могут меняться со временем
  3. Dead Letter Queue — где хранить failed events
  4. Monitoring — отслеживай задержки обработки
  5. Testing — тестируй обработчики событий отдельно

Итог

Событийно-управляемая архитектура идеальна для:

  • Систем с асинхронными операциями
  • Микросервисных архитектур
  • Систем, требующих high scalability
  • Когда нужна история всех изменений (Event Sourcing)

Но добавляет сложность в отладку и требует тщательного дизайна.