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

Что такое паттерн Event Sourcing?

2.7 Senior🔥 71 комментариев
#Архитектура и микросервисы#Брокеры сообщений и интеграция

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

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

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

Что такое паттерн Event Sourcing?

Event Sourcing (событийное проектирование или журналирование событий) — это архитектурный паттерн, при котором состояние приложения определяется как последовательность неизменяемых событий. Вместо хранения текущего состояния объекта (как в классическом подходе) мы сохраняем все события, которые привели к этому состоянию. Каждое событие представляет собой факт, который произошёл в системе, и эти события являются источником истины (source of truth). Для получения текущего состояния необходимо воспроизвести все события с начала времени.

Основные принципы Event Sourcing

  • События — это факты: Каждое событие (например, OrderCreated, ItemAddedToCart, PaymentCompleted) записывается в журнал событий (event log) и не изменяется после сохранения. Это гарантирует полную историю изменений.
  • Иммутабельность событий: События никогда не удаляются и не редактируются. Если нужно исправить ошибку, добавляется новое компенсирующее событие (например, OrderCancelled).
  • Восстановление состояния: Текущее состояние (агрегата или сущности) вычисляется путём применения всех событий из журнала к изначальному (пустому) состоянию. Этот процесс называется реплеи (replay) событий.
  • Отделение чтения от записи: Часто Event Sourcing используется в сочетании с CQRS (Command Query Responsibility Segregation), где запись (команды) работает с событиями, а чтение (запросы) использует проекции (projections), оптимизированные для конкретных представлений данных.

Пример реализации на C#

Рассмотрим упрощённый пример системы управления заказами. Определим базовые классы событий и агрегата.

// Базовый класс для всех событий
public abstract class Event
{
    public Guid Id { get; set; }
    public DateTime OccurredAt { get; set; }
    public int Version { get; set; }
}

// Конкретные события
public class OrderCreated : Event
{
    public Guid OrderId { get; set; }
    public string CustomerName { get; set; }
}

public class ItemAddedToOrder : Event
{
    public Guid OrderId { get; set; }
    public string ProductName { get; set; }
    public decimal Price { get; set; }
}

public class OrderCompleted : Event
{
    public Guid OrderId { get; set; }
}

// Агрегат "Заказ", который обрабатывает события
public class OrderAggregate
{
    public Guid Id { get; private set; }
    public string CustomerName { get; private set; }
    public List<OrderItem> Items { get; private set; } = new();
    public bool IsCompleted { get; private set; }
    public int Version { get; private set; }

    // Восстановление состояния из списка событий
    public void LoadFromHistory(IEnumerable<Event> events)
    {
        foreach (var e in events)
        {
            Apply((dynamic)e);
            Version = e.Version;
        }
    }

    // Применение событий (изменение состояния)
    private void Apply(OrderCreated e)
    {
        Id = e.OrderId;
        CustomerName = e.CustomerName;
    }

    private void Apply(ItemAddedToOrder e)
    {
        Items.Add(new OrderItem(e.ProductName, e.Price));
    }

    private void Apply(OrderCompleted e)
    {
        IsCompleted = true;
    }

    // Обработка команд и генерация новых событий
    public List<Event> CreateOrder(Guid orderId, string customerName)
    {
        var events = new List<Event>
        {
            new OrderCreated
            {
                Id = Guid.NewGuid(),
                OccurredAt = DateTime.UtcNow,
                Version = Version + 1,
                OrderId = orderId,
                CustomerName = customerName
            }
        };
        ApplyChanges(events);
        return events;
    }

    private void ApplyChanges(List<Event> events)
    {
        foreach (var e in events)
        {
            Apply((dynamic)e);
            Version = e.Version;
        }
    }
}

// Вспомогательный класс
public record OrderItem(string ProductName, decimal Price);

Преимущества Event Sourcing

  • Полный аудит и трассируемость: Все изменения системы сохраняются, что позволяет точно знать, что, когда и почему произошло. Это критически важно для финансовых, регулятивных и сложных бизнес-процессов.
  • Временные запросы: Можно восстановить состояние системы на любой момент в прошлом, чтобы проанализировать данные или отладить проблемы.
  • Гибкость чтения данных: Создавая различные проекции из одного журнала событий, можно строить специализированные представления для разных UI или отчётов без изменения модели записи.
  • Упрощение обработки сложных бизнес-процессов: События естественным образом моделируют доменную область, особенно в системах с высокой степенью асинхронности и интеграциями.
  • Отказоустойчивость и восстановление: Поскольку события неизменны, можно легко воссоздать систему после сбоя, воспроизведя журнал.

Недостатки и сложности

  • Сложность обучения: Паттерн требует смены парадигмы мышления и глубокого понимания предметной области.
  • Производительность: Воспроизведение длинной истории событий для получения текущего состояния может быть ресурсоёмким. Эта проблема решается с помощью снапшотов (snapshots) — периодического сохранения текущего состояния, чтобы реплеить только события после последнего снапшота.
  • Сложность запросов: Прямые запросы к журналу событий (например, "найти все заказы конкретного клиента") неэффективны. Необходимы отдельные проекционные базы данных, что увеличивает сложность инфраструктуры.
  • Согласованность в распределённых системах: Требуется аккуратная обработка консистентности (например, гарантия порядка событий), часто с использованием механизмов типа Event Store (специализированные БД для событий) или Apache Kafka.

Где применяется Event Sourcing?

  • Финансовые системы: Для отслеживания каждой транзакции и аудита.
  • Системы управления цепочками поставок: Где важна полная история перемещения товаров.
  • Микросервисные архитектуры: Для обеспечения надежной коммуникации между сервисами через события (Event-Driven Architecture).
  • Сложные бизнес-процессы: Например, в страховании, здравоохранении или игровых движках, где состояние зависит от множества действий.

Заключение

Event Sourcing — это мощный, но сложный паттерн, который кардинально меняет подход к хранению данных и проектированию систем. Он обеспечивает беспрецедентный уровень аудита, гибкости и надёжности, но требует тщательного проектирования и инфраструктурных инвестиций. Его использование оправдано в системах, где полная история изменений и трассируемость являются критически важными требованиями. В сочетании с CQRS и современными инструментами (такими как EventStoreDB, Apache Kafka, Axon Framework) он позволяет строить масштабируемые и отказоустойчивые приложения.

Что такое паттерн Event Sourcing? | PrepBro