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

Ответ

Event Sourcing (Хранение событий) — это архитектурный паттерн, при котором состояние приложения определяется не как текущий "снимок" данных, а как последовательность неизменяемых событий, которые привели к этому состоянию.

Ключевая идея: Вместо обновления записи в БД каждое изменение сохраняется как отдельное событие (например, OrderCreated, ItemAdded, PaymentProcessed). Текущее состояние воссоздается путем "проигрывания" (replaying) всех событий с начала.

Пример структуры событий и агрегата:

// События
record OrderCreated(String orderId, String customerId) {}
record ItemAdded(String orderId, String productId, int quantity) {}
record OrderShipped(String orderId, LocalDateTime shippedAt) {}

// Агрегат, который обрабатывает события
class OrderAggregate {
    private String orderId;
    private String status;
    private List<OrderItem> items = new ArrayList<>();

    // Метод для применения события и изменения состояния
    void apply(OrderCreated event) {
        this.orderId = event.orderId();
        this.status = "CREATED";
    }

    void apply(ItemAdded event) {
        this.items.add(new OrderItem(event.productId(), event.quantity()));
    }

    void apply(OrderShipped event) {
        this.status = "SHIPPED";
    }
}

// Восстановление состояния агрегата
OrderAggregate order = new OrderAggregate();
List<Event> history = eventStore.loadEvents(orderId);
history.forEach(event -> order.apply(event)); // Состояние восстановлено

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

  • Полный аудитлог: История всех изменений доступна по умолчанию.
  • Временные запросы: Можно восстановить состояние системы на любой момент в прошлом.
  • Гибкость: Новые представления данных (проекции) можно создать позже, обработав историю событий.
  • Упрощение в сложных доменах: Хорошо сочетается с DDD и CQRS.

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

  • Сложность запросов: Получение текущего состояния требует обработки всех событий (решается с помощью проекций — Materialized Views).
  • Миграция схемы событий: Изменение структуры старых событий требует осторожности (версионирование событий).
  • Повышенные требования к хранилищу: Необходимо хранить все события.

Типичное применение: Финансовые системы, системы управления заказами, трекеры изменений, где критически важна полная история.

Ответ 18+ 🔞

Да ты послушай, что эти умники придумали, блядь! Event Sourcing, или, по-нашему, Хранение событий. Это ж надо было так мозги вывернуть!

Представь себе, сука: вместо того чтобы просто взять и переписать бумажку в базе данных, как нормальные люди, они каждое чихание записывают в отдельную графу. Создал заказ? Пишем событие OrderCreated. Добавил товар? ItemAdded. Отправил? OrderShipped. И так до скончания века, блядь!

А потом, если хочешь узнать, что там сейчас с заказом, тебе надо, блять, взять всю эту кипу бумажек и проиграть заново, как ёбаную кассету, с самого начала. Вот, смотри, как это выглядит в коде, тут всё честно:

// События — это святое, их не меняют, блядь
record OrderCreated(String orderId, String customerId) {}
record ItemAdded(String orderId, String productId, int quantity) {}
record OrderShipped(String orderId, LocalDateTime shippedAt) {}

// А это наш главный по тарелочкам — агрегат
class OrderAggregate {
    private String orderId;
    private String status;
    private List<OrderItem> items = new ArrayList<>();

    // Сюда события прилетают, и состояние меняется
    void apply(OrderCreated event) {
        this.orderId = event.orderId();
        this.status = "CREATED";
    }

    void apply(ItemAdded event) {
        this.items.add(new OrderItem(event.productId(), event.quantity()));
    }

    void apply(OrderShipped event) {
        this.status = "SHIPPED";
    }
}

// А вот магия восстановления, ёпта!
OrderAggregate order = new OrderAggregate();
List<Event> history = eventStore.loadEvents(orderId); // Достаём всю историю
history.forEach(event -> order.apply(event)); // И проигрываем, как старый диафильм

И что же мы, блядь, с этого имеем?

  • Аудит как боженька: Кто, что, когда и куда — всё записано, спрятаться не получится. Идеально, если начальство ебёт мозг вопросами "а почему тут так?".
  • Машина времени: Хочешь узнать, как было в прошлый вторник в 14:30? Пожалуйста, отмотай события до этого момента и посмотри. Красота, ёперный театр!
  • Гибкость овердохуища: Позже придумал новый отчёт? Нет проблем, пробегись по всем старым событиям и сгенерируй его. История-то вся есть.
  • Для сложных штук — то что надо: Когда бизнес-логика такая, что мозг скукоживается, этот подход часто выручает. Особенно если ещё и CQRS рядом приплести.

Но не всё так гладко, блядь:

  • Запросы могут ебать мозг: Чтобы просто узнать текущую сумму заказа, надо всю историю пересчитать. Решается проекциями — это как заранее приготовленные сводки, чтобы не ебаться каждый раз.
  • Схемы событий — боль: Захотел поле в событие добавить? Ой, да похуй, только вот со старыми данными теперь надо что-то делать. Версионируй, мигрируй, страдай, хуле.
  • Места жрёт: Все события хранить — это не одну гигабайтную флешку захуярить. Дисков надо дохуя.

Где это всё, блядь, применяют? Да там, где история важнее всего: в банках, в бухгалтериях, в системах заказов — везде, где если что-то пошло не так, надо не орать "ой всё!", а иметь возможность пройти по шагам и найти, где именно накосячили. В общем, мощная штука, но не для всех, блядь. Если у тебя блог на WordPress, тебе это нахуй не сдалось.