Назад к вопросам
Senior
147
questionbank
Что такое шаблон Event Sourcing?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Шаблон Event Sourcing — это подход к построению приложений, в котором все изменения состояния системы сохраняются как последовательность неизменяемых событий. Вместо хранения текущего состояния объекта, сохраняется полный лог действий, которые привели к этому состоянию.
Ключевые особенности:
- Неизменяемость событий: Однажды созданное событие никогда не изменяется и не удаляется.
- Последовательность: События сохраняются в строгом хронологическом порядке.
- Архив изменений: Полная история всех изменений доступна для аудита, отладки и анализа.
- Восстановление состояния: Текущее состояние объекта может быть построено путем "воспроизведения" всех применимых событий с начала времен или определенной точки.
Преимущества:
- Неограниченное ретроспективное наблюдение: Легко увидеть, как система пришла к текущему состоянию.
- Аудит и отладка: Полная история событий облегчает поиск причин ошибок и аудит действий.
- Параллельная обработка и масштабирование: Разделение записи событий от их обработки позволяет легче масштабировать приложение.
- Поддержка CQRS (Command Query Responsibility Segregation): События часто используются для обновления read-моделей.
Недостатки:
- Сложность реализации: Требует более сложной инфраструктуры для хранения и обработки событий.
- Производительность при воспроизведении: Восстановление состояния из большого числа событий может быть медленным. Требуются оптимизации, такие как снимки состояния (snapshots).
- Изменения схем событий: Эволюция схемы событий может быть сложной задачей.
Пример:
Представьте банковский счет. В Event Sourcing вместо обновления баланса напрямую, мы сохраняем события: "Депозит X", "Снятие Y". Текущий баланс вычисляется суммированием всех операций.
// Пример класса события
public class MoneyDepositedEvent {
private final String accountId;
private final BigDecimal amount;
private final LocalDateTime timestamp;
public MoneyDepositedEvent(String accountId, BigDecimal amount, LocalDateTime timestamp) {
this.accountId = accountId;
this.amount = amount;
this.timestamp = timestamp;
}
// геттеры
}
// Пример агрегата (банковский счет), применяющего события
public class BankAccount {
private String id;
private BigDecimal balance;
private List<Object> changes = new ArrayList<>(); // список непримененных событий
public BankAccount(String id) {
this.id = id;
this.balance = BigDecimal.ZERO;
}
// Метод, генерирующий событие
public void deposit(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
MoneyDepositedEvent event = new MoneyDepositedEvent(this.id, amount, LocalDateTime.now());
apply(event); // Применяем событие к текущему состоянию
changes.add(event); // Добавляем в список изменений для сохранения
}
// Метод, применяющий событие к состоянию агрегата
private void apply(MoneyDepositedEvent event) {
this.balance = this.balance.add(event.getAmount());
}
// Метод для восстановления состояния из истории событий
public static BankAccount loadFromHistory(String id, List<Object> history) {
BankAccount account = new BankAccount(id);
for (Object event : history) {
if (event instanceof MoneyDepositedEvent) {
account.apply((MoneyDepositedEvent) event);
}
// Обработка других типов событий...
}
return account;
}
// геттеры и другие методы
}