kata academy

Событийная архитектура: как всё устроено

Подробный разбор с примером кода начинающего разработчика.

Время чтения: 3 минуты
Хочешь кодить как босс?
Заполняй форму и начни свой путь в IT прямо сейчас!
Чтобы подойти к теме Event-Driven (событийной) архитектуры, важно понимать, как в целом работают современные системы: пользователи кликают по интерфейсу, сервисы обмениваются сообщениями, устройства отправляют телеметрию, транзакции проходят каждую секунду. И системе нужно быстро и надёжно реагировать на непрерывный поток изменений.

Именно для этого используется событийная архитектура (EDA) — подход, при котором компоненты системы общаются через события, реагируя на изменения по мере их возникновения. Это работает как в распределённых системах, так и внутри монолитного приложения.
Как работает событийная архитектура: пошаговая логика
В упрощённом виде событийная архитектура всегда следует одной логике: происходит событие → событие публикуется → система реагирует. Далее разберём подробнее эти шаги.

Шаг 1. В системе происходит событие (Event)

В любой момент времени внутри приложения или сервиса может произойти действие, имеющее значение для системы. Например, клик пользователя, оформление заказа, обновление данных или сигнал от внешнего устройства. В EDA важно не само действие, а факт изменения состояния — именно он и становится событием (Event). А компонент, в котором произошло изменение, выполняет роль продюсера (Producer).

Продюсер фиксирует событие и готовит его для передачи дальше. Он не принимает решений о дальнейших действиях и не знает, какие части системы будут заинтересованы в этом событии.

Шаг 2. Событие публикуется в инфраструктуре

После фиксации событие передаётся посреднику, отвечающему за его доставку подписчикам — то есть всем элементам системы, которые могут быть заинтересованы в произошедшем событии.

В распределённых системах роль посредника обычно играет брокер сообщений (Kafka, RabbitMQ), внутри одного приложения — Event Bus. Событие, как правило,  уже содержит необходимые данные (тип, время возникновения), сформированные продюсером.

Продюсер не ждёт ответа и не знает, кто получит событие — он сразу продолжает работу. Благодаря этому система не блокируется и может обрабатывать новые действия.

Шаг 3. Событие становится доступным для потребителей (Consumers)

Опубликованное Event не отправляется одному конкретному получателю. Оно становится доступным для всех компонентов, которые подписаны на события данного типа — то есть для всех потребителей (Consumers). Каждый потребитель самостоятельно решает, нужно ли ему реагировать на произошедшее изменение.

Именно здесь проявляется ключевая особенность EDA: одно и то же событие может использоваться сразу несколькими частями системы и для разных задач.

Шаг 4. Потребители обрабатывают событие независимо друг от друга

Каждый потребитель обрабатывает событие по собственной логике. Один сервис может обновить данные, другой — запустить бизнес-процесс, третий — записать информацию в аналитическое хранилище.
Важно, что эти действия могут выполняться асинхронно — если один потребитель работает медленно или временно недоступен, это не влияет на остальных и не останавливает систему в целом.

Шаг 5. Система остаётся устойчивой и масштабируемой

Если инфраструктура поддерживает хранение событий, они не теряются при сбоях и могут быть обработаны повторно. При росте нагрузки можно добавить новых потребителей или масштабировать существующих, не изменяя логику генерации событий.
IT-калькулятор зарплат
Узнай свою рыночную зарплату за 1 минуту!
В каких проектах используется событийная архитектура?
Событийная архитектура не является универсальным решением «на все случаи жизни». Её внедряют там, где система должна непрерывно реагировать на изменения.

Электронная коммерция и онлайн-платформы
В e-commerce практически любое действие пользователя превращается в событие: добавление товара в корзину, оформление заказа, оплата, изменение статуса доставки. EDA позволяет разделить процессы: обработка заказов, складской учёт, платёжные сервисы и уведомления работают автономно, но синхронизируются через поток событий. Платформа продолжает стабильно работать даже при резком росте нагрузки, например во время распродаж.

Финансовые системы и финтех
В банковских и финтех-продуктах события лежат в основе практически всей логики. Каждая транзакция, изменение баланса, попытка входа в систему или подозрительное действие — это сигнал, который должен быть обработан немедленно. EDA позволяет обрабатывать транзакции в реальном времени, запускать антифрод-механизмы без задержек, вести аудит и журналирование всех изменений.

Телеком и сети
В телеком-системах события возникают постоянно: входящие звонки, сообщения, изменение статуса соединений. EDA позволяет обрабатывать события параллельно для миллионов пользователей, балансировать нагрузку между сервисами, мгновенно реагировать на сбои или перегрузки.

Медиа и стриминговые сервисы
В медиа и стриминге события — это клики, просмотры видео, лайки, паузы и переключение контента. Эти данные должны обрабатываться в реальном времени, чтобы сервис реагировал на действия пользователей и адаптировал контент.

Игровая индустрия
В онлайн-играх события — это действия игроков: вход, достижения, внутриигровые покупки, взаимодействия между участниками. EDA обеспечивает обработку действий миллионов пользователей одновременно, управление игровым процессом без блокировок, запуск уведомлений и обновление статистики.

Интернет вещей (IoT)
Устройства постоянно отправляют сигналы: температуру, давление, координаты, состояние оборудования. Количество таких событий может исчисляться миллионами в секунду. EDA позволяет принимать данные от множества источников, обрабатывать их параллельно и мгновенно реагировать на критические изменения.

Примеры реальных проектов, использующих EDA
  • Wildberries;
  • Ozon;
  • Яндекс;
  • Самокат;
  • МТС;
  • Сбер;
  • X5;
  • Avito;
  • CDEK;
  • VK.
Твой первый оффер: зарплата от 120 000 рублей! Пройди курс по Go-разработке с гарантией трудоустройства и получи такое предложение от работодателя! Основная оплата курса проходит уже после выхода на работу, ты платишь за полученный результат!

Инструменты для событийной архитектуры
EDA требует специальных инструментов для обработки events, их маршрутизации и масштабирования систем, среди них:
  • программные решения;
  • архитектурные паттерны или методологии, которые можно реализовать с помощью разных инструментов и языков программирования.
Ниже собраны основные технологии и подходы, которые чаще всего используют в EDA.

Инструменты

1. Message Broker (брокеры сообщений)
Назначение: обеспечивают асинхронную доставку сообщений между продюсерами и потребителями, гарантируют, что события не потеряются.
Популярные решения
  • Apache Kafka — высокопроизводительная платформа для потоковой обработки данных, поддерживает pub/sub и стриминг событий.
  • RabbitMQ — лёгкий брокер для очередей сообщений, удобен для микросервисов и небольших систем.
  • Apache Pulsar — современный брокер сообщений с поддержкой потоковой передачи и многотопиковых подписок.
  • Redis Streams — быстрый инструмент для очередей и потоков в памяти, подходит для real-time обработки.

2. Stream Processing (обработка потоков данных)
Назначение: обработка непрерывного потока событий в реальном времени. Например, мониторинг действий пользователей, real-time аналитика, генерация уведомлений и триггеров.
Популярные решения
  • Apache Flink — для масштабируемой и низколатентной обработки потоков.
  • Apache Spark Structured Streaming — обработка больших потоков данных и агрегирование.
  • Kafka Streams — встроенный инструмент для потоковой обработки внутри Kafka.

3. Системы управления событиями и оркестрации
Назначение: управляют потоком событий, маршрутизацией и обработкой задач между компонентами.
Популярные решения
  • AWS EventBridge — маршрутизация событий в облачной инфраструктуре.
  • Camunda или Zeebe — управление бизнес-процессами с событиями.
  • Apache NiFi — маршрутизация, трансформация и потоковая обработка событий.

4. Контейнеризация и оркестрация сервисов
Назначение: поддерживает масштабируемость EDA через контейнеры и динамическое управление сервисами.
Популярные решения
  • Docker — контейнеризация сервисов.
  • Kubernetes — управление масштабированием и отказоустойчивостью микросервисов.
  • Helm или Istio — оркестрация и маршрутизация внутри кластера.

Паттерны

1. Event Sourcing (событийное хранение)
Назначение: вместо того чтобы хранить текущее состояние объекта, система фиксирует все события, которые его изменяют. Например, вместо того чтобы хранить текущий баланс счёта, система хранит все операции: пополнение, списание, возврат. Текущее состояние всегда можно восстановить, воспроизведя цепочку событий.
Преимущества:
  • полный аудит действий;
  • возможность восстановить состояние системы на любой момент времени;
  • лёгкая интеграция с аналитикой и потоковой обработкой.
Событийное хранения можно построить на EventStoreDB, PostgreSQL, MongoDB или другом хранилище, которое поддерживает последовательное хранение событий.

2. CQRS
Назначение: разделяет операции записи (команды) и чтения (запросы) данных. Например, команда обновления статуса заказа создаёт событие, а аналитическая часть системы формирует отчёты и дашборды на основе этих событий.
Преимущества:
  • ускоряет обработку запросов;
  • позволяет масштабировать чтение и запись отдельно;
  • упрощает построение сложных read-моделей под конкретные задачи.
CQRS можно реализовать с помощью Axon Framework, NestJS + TypeORM, .NET + MediatR или через собственные микросервисы с брокерами сообщений.
Пример кода событийной архитектуры на Go
Этот пример демонстрирует базовую реализацию событийной архитектуры с использованием EventBus. В реальных системах его роль выполняет Message Broker (Kafka, RabbitMQ), который обеспечивает персистентность событий, масштабирование и надёжную доставку между сервисами.
package main

import (
	"fmt"
	"sync"
	"time"
)

// --- Event ---
type Event struct {
	Name string
	Data any
}

// --- Event Bus ---
type EventBus struct {
	mu       sync.RWMutex
	handlers map[string][]func(Event)
}

func NewEventBus() *EventBus {
	return &EventBus{handlers: make(map[string][]func(Event))}
}

func (b *EventBus) Subscribe(eventName string, handler func(Event)) {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.handlers[eventName] = append(b.handlers[eventName], handler)
}

func (b *EventBus) Publish(event Event) {
	b.mu.RLock()
	// Копируем обработчики, чтобы избежать гонок при публикации и подписке одновременно
	handlers := make([]func(Event), len(b.handlers[event.Name]))
	copy(handlers, b.handlers[event.Name])
	b.mu.RUnlock()

	for _, h := range handlers {
		go h(event) // асинхронная обработка
	}
}

// --- Использование ---
func main() {
	bus := NewEventBus()

	// Consumer 1: отправка email
	bus.Subscribe("OrderCreated", func(e Event) {
		fmt.Printf("📧 Отправляем email для заказа: %v\n", e.Data)
	})

	// Consumer 2: обновление склада
	bus.Subscribe("OrderCreated", func(e Event) {
		fmt.Printf("📦 Обновляем склад для заказа: %v\n", e.Data)
	})

	// Producer
	bus.Publish(Event{Name: "OrderCreated", Data: "order-123"})

	// Даем время обработчикам завершиться
	// В реальном приложении лучше использовать синхронизацию, например, WaitGroup
	time.Sleep(1 * time.Second)
}
Пояснение для новичка
1. Event — что такое событие
type Event struct {
    Name string
    Data any
}
  • Event — структура, описывающая событие.
  • Name — имя события (например, OrderCreated).
  • Data — полезная нагрузка (например, ID заказа). Тип any позволяет передавать любые данные.
В реальных системах здесь могут быть JSON-данные, метаданные, временные метки и т.д.

2. EventBus — шина событий
type EventBus struct {
    mu       sync.RWMutex
    handlers map[string][]func(Event)
}
  • EventBus — центральный компонент, через который проходят все события.
  • handlers — карта, где ключ это имя события, а значение — список обработчиков, подписанных на него.
  • sync.RWMutex обеспечивает безопасную работу при одновременной подписке и публикации.

3. Producer (Продюсер)
bus.Publish(Event{Name: "OrderCreated", Data: "order-123"})
  • Продюсер создаёт событие и публикует его в шину.
  • Он не знает, кто будет обрабатывать событие и когда.
  • Его задача — зафиксировать факт изменения состояния.
Это ключевая идея EDA: создание события отделено от его обработки.

4. Consumer (Потребитель)
bus.Subscribe("OrderCreated", func(e Event) {
    fmt.Printf("📧 Отправляем email для заказа: %v\n", e.Data)
})
  • Потребитель подписывается на события определённого типа и реагирует на них по своей логике.
  • В нашем примере два независимых потребителя обрабатывают одно и то же событие: один отправляет email, другой обновляет склад.
  • Каждый обработчик запускается в отдельной горутине — то есть работает асинхронно.
5. Асинхронность и масштабируемость
  • Продюсер и потребители работают независимо друг от друга.
  • Одно событие может обработать сразу несколько потребителей параллельно.
  • Медленный потребитель не блокирует остальных и не мешает публикации новых событий.
Именно так ведут себя реальные Event-Driven системы с Kafka, RabbitMQ или NATS — только роль EventBus там играет брокер сообщений.
Когда начинающему разработчику начинать изучать событийную архитектуру
К EDA стоит приступать, если у тебя уже есть база программирования. Сначала пройди эти темы: циклы, функции, классы, коллекции, потоки (Java) или горутины (Go). Затем разберись с простыми веб-приложениями: клиент-серверная модель, REST API, обработка запросов и данных. После этого можно переходить к EDA: сначала простые очереди и события в коде, потом брокеры сообщений и Stream Processing.

Если ты хочешь изучить EDA на профессиональном уровне и устроиться на работу, приглашаем тебя на курс «Go-разработчик». Обучение за 9 месяцев: с нуля до выхода на работу. Трудоустройство с зарплатой от 120 000 рублей гарантировано договором со школой. Узнай подробности на нашем сайте.

Статьи для старта в IT

Истории наших выпускников

Стань тем, кто задаёт тон в IT!
Подпишись на нашу рассылку и первым получай статьи по Java, JavaScript, Golang и QA. Позволь себе быть экспертом!