Learning Platform
Глоссарий Troubleshooting
Урок 02.03 · 20 мин
Средний
Design PatternsDistributed SystemsEvent SourcingCQRSSaga

Design Patterns для распределённых систем

Зачем паттерны

Design patterns — это проверенные решения для повторяющихся проблем. В распределённых системах (и в блокчейне) одни и те же проблемы возникают снова и снова:

  • Как гарантировать доставку сообщений?
  • Как обработать частичный сбой многошаговой операции?
  • Как разделить чтение и запись для масштабирования?
  • Как восстановить состояние после сбоя?

Pattern 1: Event Sourcing

Проблема: Как хранить историю изменений состояния, чтобы можно было восстановить любое прошлое состояние?

Решение: Вместо хранения текущего состояния — хранить последовательность событий, которые привели к нему.

Традиционный подход:
balance = 100  (перезаписывается при каждой операции)

Event Sourcing:
Event 1: deposit(50)   → balance: 50
Event 2: deposit(100)  → balance: 150
Event 3: withdraw(50)  → balance: 100

Применение к TON

Блокчейн — это Event Sourcing по определению. Каждая транзакция — это событие. Состояние контракта — результат применения всех транзакций от genesis block.

Event Sourcing в TON
Deploy
msg: deposit(50)
msg: deposit(100)
msg: withdraw(50)
State: balance=100

Design Insight: Поскольку блокчейн уже реализует Event Sourcing на уровне протокола, ваши контракты должны быть идемпотентными — повторная обработка одного и того же события не должна ломать состояние.

Pattern 2: Saga Pattern

Проблема: Как выполнить многошаговую операцию, когда каждый шаг может отказать, а откатить блокчейн-транзакцию невозможно?

Решение: Разбить операцию на шаги. Каждый шаг имеет компенсирующую транзакцию. При сбое выполняются компенсации в обратном порядке.

Применение к TON: DEX Swap

DEX swap на TON — классическая Saga:

Saga Pattern: DEX Swap на TON
Шаг 1
Шаг 2
Шаг 3
WARNING

Критически важно в TON

Каждый шаг Saga в TON — это отдельное асинхронное сообщение. Между шагами 1 и 2 может пройти время, за которое курс изменится. Поэтому DEX контракты используют slippage tolerance — максимальное допустимое отклонение курса.

Если шаг 3 не удался (например, у DEX не хватило Token B), контракт должен автоматически отправить bounce message для компенсации шага 1.

Pattern 3: CQRS (Command Query Responsibility Segregation)

Проблема: Чтение и запись имеют разные требования к производительности и масштабированию.

Решение: Разделить модель на Command (запись/изменение) и Query (чтение).

Применение к TON

В TON это разделение встроено на уровне протокола:

ОперацияТипСтоимостьМодификация
Отправка сообщенияCommandПлатная (gas)Изменяет state
Get-method вызовQueryБесплатнаяТолько чтение
Command path (on-chain):
User → external message → Contract → state change → new block

Query path (off-chain):
dApp → lite-server → get_method() → return data (бесплатно, мгновенно)

Design Insight: При проектировании TON-контрактов всегда разделяйте логику изменения состояния (handlers) и логику чтения (get-methods). Get-methods должны предоставлять все данные, нужные фронтенду, без необходимости отправлять on-chain транзакции.

Pattern 4: Circuit Breaker

Проблема: Один неисправный компонент вызывает каскадный отказ всей системы.

Решение: При обнаружении сбоя — «разомкнуть» цепь: прекратить отправку запросов к неисправному компоненту и вернуть быстрый ответ об ошибке.

Применение к TON

В TON circuit breaker реализуется через bounce messages:

Normal flow:
ContractA → message → ContractB → обработка → ответ

Bounce flow (circuit breaker):
ContractA → message → ContractB → FAIL (исключение)

                              bounce message → ContractA

                           ContractA обрабатывает bounce → компенсация
NOTE

Bounce = автоматический Circuit Breaker

В TON, если контракт не смог обработать входящее сообщение и бросил исключение, система автоматически отправляет bounce message обратно отправителю. Это встроенный circuit breaker — отправитель узнаёт о проблеме и может отреагировать (вернуть средства, повторить операцию, записать ошибку).

Но важно: bounce работает только для internal messages с флагом bounce: true. Если вы не установили этот флаг, средства будут потеряны при сбое.

Pattern 5: Idempotency

Проблема: Сообщение может быть доставлено повторно. Повторная обработка не должна ломать состояние.

Решение: Каждая операция должна давать одинаковый результат при многократном выполнении.

Применение к TON

В TON сообщения имеют query_id — уникальный идентификатор. Контракт может хранить processed_queries и игнорировать повторные:

// Псевдокод TON-контракта с идемпотентностью
receive(msg) {
  let query_id = msg.query_id;
  
  // Проверяем, не обработали ли уже
  if (self.processed.has(query_id)) {
    return; // Идемпотентность: повторное сообщение игнорируется
  }
  
  // Обрабатываем
  self.balance += msg.amount;
  self.processed.add(query_id);
}
WARNING

Trade-off: идемпотентность vs storage fee

Хранение processed_queries в контракте стоит денег (storage fee). В production-системах обычно хранят только последние N query_id или используют timestamp-based подход вместо полного множества.

Сводная таблица паттернов

Design Patterns: Применение к TON
Pattern
Классика
TON
Модуль
Event Sourcing
Saga
CQRS
Circuit Breaker
Idempotency
Проверка знанийKnowledge check
ОтветAnswer

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. DEX на TON выполняет swap: пользователь отправляет Token A, получает Token B. Если на шаге выдачи Token B произошла ошибка, какой design pattern обеспечивает возврат Token A пользователю?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5