Примеры моделирования — Gap-анализ, Saga, Compensation

Урок 3 из 3

Урок 05.03: Примеры моделирования — Gap-анализ, Saga, Compensation

Цель урока

Научиться применять BPMN для реальных задач аналитика. После этого урока вы сможете:

  • Проводить Gap-анализ: от AS-IS (как есть) к TO-BE (как будет) с выявлением проблем и точек автоматизации
  • Проектировать Saga-процессы с компенсациями для микросервисной архитектуры
  • Моделировать откаты распределённых транзакций (бронирование + оплата + компенсация)
  • Понимать роль системного аналитика при проектировании надёжных процессов с восстановлением после сбоев
  • Применять продвинутые паттерны: эскалация, цикл, компенсация, хореография

1. Gap-анализ: переход от AS-IS к TO-BE

1.1. Что такое Gap-анализ?

Gap-анализ (анализ разрывов) — метод сравнения текущего состояния процесса (AS-IS) с целевым (TO-BE) с целью выявления «разрывов» — проблем, узких мест, потерь, которые нужно устранить.

Процесс в картинке:

Текущее состояние (AS-IS)          Целевое состояние (TO-BE)
     ╔════════════╗                     ╔════════════╗
     ║             ║                     ║             ║
     ║   Хаос,     ║                     ║ Автомати-   ║
     ║   email,    ║                     ║ зированный  ║
     ║   Excel,    ║    ─── Gap ───►    ║ процесс с   ║
     ║   потери    ║                     ║ SLA, API,   ║
     ║             ║                     ║ аудит       ║
     ╚════════════╝                     ╚════════════╝
           │                                    │
           ▼                                    ▼
     Проблемы:                           Решения:
     • Всё вручную                       • Автоматизация
     • Нет SLA                           • BPMN-движок
     • Нет аудита                        • Интеграция
     • Нет метрик                        • Метрики и SLA

1.2. Зачем аналитику моделировать AS-IS?

Аргумент 1: Вы не можете улучшить то, чего не понимаете

Если вы не смоделировали текущий процесс — вы не знаете, где на самом деле «узкие места». Заказчик может сказать: «У нас всё плохо с закупками». AS-IS покажет: проблема не в закупках, а в том, что руководитель отвечает по email и теряет письма.

Аргумент 2: Объективная база для улучшений

AS-IS — это «доказательство» для заказчика. Вы показываете диаграмму и говорите:

  • «Вот здесь вы теряете 3 дня на ручной ввод»
  • «Здесь 30% заявок теряются из-за отсутствия единой системы»
  • «Вот здесь нет SLA — заявка может висеть неделями»

Аргумент 3: Измерение результата

После внедрения TO-BE вы возвращаетесь к AS-IS и сравниваете: «До: 5 дней на согласование. После: 2 часа». Без AS-IS нечем доказать эффективность.

Аргумент 4: Согласование с заказчиком

Вы рисуете AS-IS и показываете заказчику: «Правильно я понял ваш процесс?». Заказчик: «Нет, у нас ещё бухгалтер проверяет бюджет вручную». Вы исправляете — и только потом рисуете TO-BE. Иначе TO-BE будет построен на неверном фундаменте.

1.3. Методика проведения Gap-анализа

Шаг 1: Сбор информации

  • Интервью с участниками процесса (роли, шаги, болевые точки)
  • Анализ документов (регламенты, инструкции, формы)
  • Наблюдение (как реально работают люди)
  • Метрики (сколько времени занимает шаг, сколько ошибок)

Шаг 2: Моделирование AS-IS

  • Нарисовать BPMN-диаграмму текущего процесса
  • Включить все «ручные» шаги (Manual Task)
  • Показать все точки взаимодействия (email, личные встречи, мессенджеры)
  • Указать все «бутылочные горлышки» (нотами, цветом)

Шаг 3: Анализ «разрывов» (Gap Analysis)

Таблица проблем:

Проблема Где в AS-IS Риск Тип
1 Действие выполняется вручную User/Manual Task Ошибки, задержки Автоматизация
2 Нет системы — всё через email Нет Service Task Потеря данных, нет аудита Система
3 Нет SLA Нет Boundary Timer Задача «зависает» SLA
4 Нет альтернативы при ошибке Нет Error Boundary Процесс падает Обработка ошибок
5 Дублирование данных Ручной ввод в 2+ систем Ошибки, потери Интеграция
6 Нет эскалации Нет Timer Решения не принимаются Эскалация

Шаг 4: Проектирование TO-BE

На основе проблем из Gap Analysis рисуете TO-BE. Для каждой проблемы — решение:

  • Автоматизация: заменяем Manual Task на Service Task
  • Система: добавляем единую систему (CRM) и BPMN-движок
  • SLA: добавляем Boundary Timer с эскалацией
  • Обработка ошибок: добавляем Error Boundary с компенсацией
  • Интеграция: заменяем «человек переносит данные» на Service Task с API
  • Эскалация: добавляем Timer → Send Task / Call Activity

Шаг 5: Валидация TO-BE

  • Проверяем, что все проблемы из Gap Analysis решены
  • Показываем TO-BE заказчику: «Вот как будет. Устраивает?»
  • Сравниваем AS-IS и TO-BE: метрики «было / стало»

1.4. AS-IS vs TO-BE: шаблон отчёта

Для каждого проекта аналитик готовит таблицу-отчёт:

Аспект AS-IS (было) TO-BE (стало) Улучшение
Время согласования заявки 5 дней (в среднем) 2 часа 96% быстрее
Доля потерянных заявок 15% 0% Устранены потери
Ручные операции 8 (из 12 шагов) 2 (из 10 шагов) Автоматизация 75%
SLA на шаги Нет Есть (1ч, 24ч, 48ч) Прозрачность
Участники 3 системы (email, Excel, 1С) 1 система (CRM + интеграция) Единое окно
Аудит Нет Полный журнал Соответствие

2. Пример Gap-анализа: Закупки в компании

2.1. AS-IS: Описание текущего процесса

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

Участники:

  • Сотрудник (инициатор) — хочет купить что-то для работы
  • Руководитель отдела — согласовывает необходимость
  • Финансовый директор — согласовывает бюджет (если сумма > 50 000 ₽)
  • Бухгалтер — вводит заявку в учётную систему (1С)

Описание AS-IS:

  1. Сотруднику нужно купить товар (например, ноутбук)
  2. Сотрудник находит товар в интернет-магазине, копирует ссылку
  3. Сотрудник пишет письмо руководителю: «Купить ноутбук за 75 000 ₽? Ссылка...»
  4. Руководитель читает письмо, отвечает «Ок, давай» или «Нет, не нужно»
  5. Если ответ «Нет» — процесс завершён
  6. Если ответ «Да» — сотрудник проверяет сумму
  7. Если сумма ≤ 50 000 ₽ — сотрудник печатает заявку, несёт в бухгалтерию
  8. Если сумма > 50 000 ₽ — сотрудник идёт к финансовому директору лично
  9. Финдиректор (если нужно) смотрит бюджет, говорит «Да/Нет»
  10. Если финдиректор согласен — сотрудник печатает заявку, несёт в бухгалтерию
  11. Бухгалтер получает бумажную заявку, вручную вводит данные в 1С
  12. Заявка создана в 1С, сотрудник может покупать

2.2. BPMN-диаграмма AS-IS

┌─────────────────────────────────────────────────────────────────────────────┐
│ Pool: Компания (AS-IS)                                                       │
│                                                                             │
│ Lane: Сотрудник                                                              │
│ ○ (None) → ⬭ Найти товар, скопировать ссылку                                │
│   → ⬭ Написать email руководителю                                           │
│   → ◯✉ (Catch: Ожидать ответ)                                               │
│   → ◇ XOR [Согласовано?]                                                     │
│     ├── [Нет] → ◉ (End: Отказ)                                              │
│     └── [Да] → ◇ XOR [Сумма > 50 000?]                                      │
│           ├── [Да] → ⬭ Идти к финдиректору (лично)                          │
│           │        → ◯✉ (Catch: Ожидать решение)                             │
│           │        → ◇ XOR [Финдиректор согласен?]                           │
│           │          ├── [Нет] → ◉ (End: Отказ)                              │
│           │          └── [Да] → ⬭ Отнести заявку в бухгалтерию (бумага)     │
│           └── [Нет] → ⬭ Распечатать заявку, отнести в бухгалтерию           │
│                                                                             │
│ Lane: Руководитель                                                           │
│ ◯✉ (Catch: Письмо) → ⬭ Прочитать, решить → ⬭ Ответить email                │
│                                                                             │
│ Lane: Финансовый директор (Lane появляется только если сумма > 50K)          │
│ ◯✉ (Catch: Визит сотрудника) → ⬭ Проверить бюджет устно → ⬭ Сказать "Да/Нет"│
│                                                                             │
│ Lane: Бухгалтер                                                              │
│ ⬭ User: Ввести данные из бумаги в 1С → ◉ (End)                              │
└─────────────────────────────────────────────────────────────────────────────┘

2.3. Gap Analysis: проблемы AS-IS

Проблема Риски Где в AS-IS Метрика
1 Нет единой системы — всё через email и личные встречи Потеря заявок (письмо не дошло, пролистали), нет аудита Lane Сотрудник → «Написать email»; Lane Руководитель → «Ответить email» 15% заявок теряется
2 Ручной ввод в 1С бухгалтером Ошибки ввода (не тот артикул, не та сумма), задержка до 2 дней Lane Бухгалтер → «Ввести данные в 1С» 3–5 ошибок на 100 заявок
3 Личное хождение к финдиректору Потеря рабочего времени, задержка до недели (не поймать в кабинете) Lane Сотрудник → «Идти к финдиректору» 1–5 дней на шаг
4 Нет SLA / тайм-аутов Руководитель может отвечать неделю — нет механизма эскалации ◯✉ «Ожидать ответ» — без таймера Заявка «зависает» на 5+ дней
5 Нет альтернативы при отказе Сотрудник не знает причину отказа — повторяет ту же ошибку ◇ XOR [Нет] → ◉ End (без объяснения) Нет обратной связи
6 Нет метрик Нельзя измерить, сколько времени занимает процесс Нет Boundary Timer Невозможно улучшить
7 Бумажный документооборот Потеря бумаг, затраты на печать «Распечатать заявку», «отнести» 2 листа × 100 заявок = 200 листов/мес

2.4. TO-BE: Целевой процесс

Что меняется в TO-BE:

  1. Внедряется единая система (CRM с BPMN-движком)
  2. Заявки создаются в веб-форме, не в email
  3. Проверка бюджета — автоматическая (DMN-таблица) или электронное согласование
  4. 1С интегрируется через API — Service Task
  5. На каждый шаг — SLA с таймаутами и эскалацией
  6. Решения фиксируются с комментариями

2.5. BPMN-диаграмма TO-BE

┌─────────────────────────────────────────────────────────────────────────────┐
│ Pool: Компания (TO-BE)                                                       │
│                                                                             │
│ Lane: Сотрудник                                                              │
│ ○✉ (Start: Сообщение от сотрудника — «Создать заявку»)                       │
│ → ⬭ User: Заполнить форму заявки (веб-форма в CRM)                         │
│ → ◉✉ (End: Уведомление о результате)                                         │
│                                                                             │
│ Lane: Система (CRM + BPMN-движок)                                           │
│ ⬭ Service: Проверить наличие товара на складе (WMS API)                     │
│ → ◇ XOR [Товар есть на складе?]                                              │
│   ├── [Да] → ⬭ Service: Отклонить заявку (товар уже есть)                   │
│   │          → ◉ (End: Отказ)                                                │
│   └── [Нет] → ⬭ Business Rule: Проверить бюджет сотрудника (DMN)            │
│               → ◇ XOR [Бюджет достаточен?]                                   │
│                 ├── [Да] → ⬭ User: Согласовать (Руководитель)               │
│                 └── [Нет] → ⬭ Send: Уведомить руководителя о нехватке        │
│                              → ⬭ User: Согласовать с доп.финансированием    │
│                                                                             │
│ Lane: Руководитель                                                           │
│ ⬭ User: Рассмотреть заявку, принять решение                                │
│ │ ◯⏰ (Boundary Timer: 24ч — Non-Interrupting → напоминание)               │
│ │ ◯⏰ (Boundary Timer: 48ч — Interrupting → эскалация вышестоящему)         │
│ → ◇ XOR [Решение]                                                           │
│   ├── [Согласовано] → ◇ XOR [Сумма > 50 000?]                              │
│   │                   ├── [Да] → ⬭ User: Согласовать (Финдиректор)          │
│   │                   └── [Нет] → ⬭ Service: Создать закупку в 1С (API)     │
│   └── [Отказано] → ⬭ Service: Уведомить сотрудника с указанием причины     │
│                    → ◉ (End: Отказ)                                          │
│                                                                             │
│ Lane: Финансовый директор                                                    │
│ ⬭ User: Проверить бюджет, согласовать                                      │
│ │ ◯⏰ (Boundary Timer: 48ч — Interrupting → авто-отказ)                    │
│ → ◇ XOR [Решение]                                                           │
│   ├── [Да] → ⬭ Service: Создать закупку в 1С (API)                         │
│   └── [Нет] → ⬭ Service: Уведомить → ◉ (End: Отказ)                        │
│                                                                             │
│ Lane: 1С (внешняя система, свёрнутый пул)                                    │
│ → ⬭ Service (принять данные) → ◉                                           │
└─────────────────────────────────────────────────────────────────────────────┘

2.6. Таблица: AS-IS → TO-BE (сопоставление)

Проблема AS-IS Решение TO-BE BPMN-элемент TO-BE
1 Email вместо системы CRM + BPMN-движок User Task с формой
2 Ручной ввод в 1С API-интеграция Service Task
3 Личное хождение к финдиректору Электронное согласование User Task в CRM
4 Нет SLA Таймеры и эскалация Boundary Timer (24ч/48ч)
5 Нет причины отказа Уведомление с причиной Service Task → Send Task
6 Нет метрик BPMN-движок собирает метрики Boundary Timer
7 Бумага ЭДО (электронный архив) Data Store (БД)

Метрики «Было / Стало»:

Метрика AS-IS TO-BE Улучшение
Среднее время согласования 5 дней 4 часа ×30 быстрее
Доля потерянных заявок 15% < 1% ×15 меньше
Ручных операций 7 из 7 2 из 7 Авто 70%
Прозрачность (аудит) Нет Полный Да

3. Паттерн «Эскалация» (Escalation) — детальный разбор

3.1. Проблема

Задача назначена на пользователя. Пользователь не реагирует (в отпуске, загружен, проигнорировал). Процесс останавливается на часы или дни.

3.2. Решение: Boundary Timer с эскалацией

Одиночная эскалация (уровень 1):

⬭ User: "Согласовать заявку"
│
◯⏰ (Boundary Timer, Interrupting, 48ч)
│
▼
⬭ Service: "Эскалировать вышестоящему руководителю"

Множественная эскалация (уровни 2+):

⬭ User: "Согласовать заявку"
│
◯⏰ (24ч, Non-Interrupting) → ⬭ Send: "Напоминание исполнителю"
│
◯⏰ (48ч, Interrupting) → ⬭ User: "Эскалация: руководителю отдела"
│                          │
│                          ◯⏰ (24ч, Non-Interrupting) → напоминание
│                          │
│                          ◯⏰ (72ч, Interrupting) → ⬭ User: "Директору департамента"

3.3. Три стратегии эскалации

Стратегия Как работает Когда использовать
Переадресация (Reassign) Задача снимается с текущего исполнителя и назначается другому (вышестоящему) Исполнитель не справляется / в отпуске
Автоматическое решение (Auto-escalate) Если не ответил за N дней — заявка автоматически согласовывается Некритичные заявки, мелкие суммы
Параллельное уведомление (Notify) Задача остаётся у исполнителя, но руководитель получает уведомление Нужно проконтролировать, но не снимать задачу

Какая стратегия когда:

  • Закупка до 10 000 ₽ — Auto-escalate (если не ответил за 3 дня → согласовано)
  • Закупка до 100 000 ₽ — Reassign (3 дня → руководителю, ещё 3 → директору)
  • Закупка > 100 000 ₽ — Notify + Reassign (2 дня напоминание, 5 — переадресация)

4. Паттерн «Компенсация» (Compensation) — глубокий разбор

4.1. Проблема

В длительном процессе есть шаги, которые уже выполнены. Если на позднем шаге возникает ошибка — нужно «откатить» предыдущие. Это компенсация.

Аналогия: Вы собрали чемодан (шаг 1), сели в такси (шаг 2), приехали в аэропорт (шаг 3), а рейс отменили (событие). Нужно «откатить»: выйти из такси (компенсация шага 2), разобрать чемодан (компенсация шага 1).

4.2. Элементы BPMN для компенсации

Элемент Обозначение Смысл
Compensation Boundary Event ⚖ (на границе задачи) Обработчик компенсации: что делать для отката этой задачи
Compensation Intermediate Throw ◯⚖ (в потоке) Запуск компенсации вручную
Compensation End Event ◉⚖ Завершение подпроцесса с запуском компенсации
Compensation Task ⬭⚖ Действие отката (обычно скрыто, вызывается только при компенсации)

4.3. Как работает компенсация в BPMN

Шаг 1: Процесс идёт вперёд
   ⬭ A (резерв) → ⬭ B (списание) → ⬭ C (подтверждение)

Шаг 2: На шаге C произошла ошибка
   ⬭ A → ⬭ B → ⬭ C → ❌ (ошибка)

Шаг 3: Запускается компенсация в ОБРАТНОМ порядке
   ⬭ A → ⬭ B → ⬭ C → ❌
                ⬭ B ⚖ (отмена списания)   ← первая компенсация
   ⬭ A ⚖ (отмена резерва)                 ← вторая компенсация

Ключевой принцип: Компенсация идёт в обратном порядке (reverse order). Последний успешный шаг откатывается первым.

4.4. Пример компенсации: заказ в интернет-магазине

┌─────────────────────────────────────────────────────────────────────┐
│ ⬔ Подпроцесс: Обработать заказ                                      ╗
│                                                                     │
│ ⬭ Service: "Зарезервировать товар на складе"                       │
│ │  ⚖ (Compensation Boundary → ⬭⚖ "Отменить резерв")               │
│ ↓                                                                   │
│ ⬭ Service: "Списать средства с карты"                              │
│ │  ⚖ (Compensation Boundary → ⬭⚖ "Вернуть средства")               │
│ ↓                                                                   │
│ ⬭ Service: "Отправить заказ в доставку"                            │
│ │                                                                   │
│ ◇ XOR [Доставка подтверждена?]                                      │
│ ├── [Да] → ◉ (End: Успех)                                          │
│ └── [Нет] → ◉⚖ (Compensation End — запуск отката)                  │
│                                                                     │
│ (При срабатывании Compensation End:                                  │
│  1. Отменить резерв товара                                          │
│  2. Вернуть средства на карту)                                      │
└─────────────────────────────────────────────────────────────────────┘

5. ⭐ Saga-паттерн с компенсацией: бронирование отеля + билеты + списание денег

5.1. Бизнес-контекст

Туристический сервис. Пользователь бронирует:

  1. Отель — брон. номера
  2. Авиабилеты — покупка билетов
  3. Списание денег — оплата с карты

Проблема: Это распределённая транзакция через 3 микросервиса:

  • Booking Service (отель)
  • Ticket Service (билеты)
  • Payment Service (деньги)

Если, например, билеты куплены и деньги списаны, а отель оказался недоступен — нужно откатить билеты и вернуть деньги.

5.2. AS-IS: как может быть без Saga (и почему это плохо)

Вариант «в лоб» (без компенсаций):
⬭ Service: Забронировать отель (Booking API)
⬭ Service: Купить билеты (Ticket API)
⬭ Service: Списать деньги (Payment API)

Если списание денег упало:
  — отель мы уже забронировали (занял место, не отменили)
  — билеты уже куплены (деньги ушли авиакомпании)
  — пользователь НЕ может отменить ни то, ни другое

Последствия:

  • Отель забронирован, но не отменён → штраф
  • Билеты куплены → деньги ушли
  • Пользователь звонит в поддержку
  • Поддержка вручную отменяет отель и билеты
  • Время: часы, деньги: потери на комиссию за отмену

5.3. TO-BE: Saga с компенсациями

⬭ Service: "Забронировать отель" (Booking Service)
│  ⚖ (Compensation → ⬭⚖ "Отменить бронь отеля")
│
⬭ Service: "Забронировать билеты" (Ticket Service)
│  ⚖ (Compensation → ⬭⚖ "Отменить бронь билетов")
│
⬭ Service: "Списать деньги" (Payment Service)
│  ⚖ (Compensation Boundary → ⬭⚖ "Вернуть деньги")
│
◇ XOR [Статус списания?]
├── [Успех] → ⬭ Service: "Подтвердить бронь отеля"
│             → ⬭ Service: "Подтвердить билеты"
│             → ⬭ Send: "Отправить подтверждение клиенту"
│             → ◉ (End: Успех)
│
└── [Ошибка: карта declined] → ◉⚖ (Compensation End)
                            ┌─────────────────────────────
                            │ Запускается компенсация:
                            │ 1. ⬭⚖ "Вернуть деньги" (если частично списалось)
                            │ 2. ⬭⚖ "Отменить бронь билетов"
                            │ 3. ⬭⚖ "Отменить бронь отеля"
                            │
                            → ⬭ Service: "Уведомить клиента об ошибке"
                            → ◉ (End: Ошибка)

Что происходит при ошибке на шаге «Списать деньги»:

Пошагово:

t=1:  ⬭ Забронировать отель → ✅ Успех
        Состояние: отель = ЗАБРОНИРОВАН, билеты = НЕТ, деньги = НЕТ

t=2:  ⬭ Купить билеты → ✅ Успех
        Состояние: отель = ЗАБРОНИРОВАН, билеты = КУПЛЕНЫ, деньги = НЕТ

t=3:  ⬭ Списать деньги → ❌ Ошибка (карта declined)

t=4:  ◉⚖ Compensation End активирован

t=5:  ⚖ Отменить бронь билетов (Ticket Service API: cancelBooking(ticketId))
        → ✅ Подтверждение от Ticket Service
        Состояние: отель = ЗАБРОНИРОВАН, билеты = ОТМЕНЕНЫ, деньги = НЕТ

t=6:  ⚖ Отменить бронь отеля (Booking Service API: cancelReservation(hotelId))
        → ✅ Подтверждение от Booking Service
        Состояние: отель = ОТМЕНЕН, билеты = ОТМЕНЕНЫ, деньги = НЕТ

t=7:  ⬭ Уведомить клиента
        → "Ваш заказ не оформлен. Причина: карта declined. Попробуйте другую карту."

5.4. Полный BPMN-фрагмент Saga

@startuml
start
:Забронировать отель;
:Купить билеты;
:Списать деньги;

if (Статус списания?) then (Успех)
  :Подтвердить бронь отеля;
  :Подтвердить билеты;
  :Отправить подтверждение клиенту;
  stop
else (Ошибка)
  :Компенсация: Вернуть деньги;
  :Компенсация: Отменить билеты;
  :Компенсация: Отменить отель;
  :Уведомить клиента об ошибке;
  stop
endif
@enduml

5.5. Что реально происходит в BPMN XML для Compensation

<bpmn:subProcess id="saga_booking" name="Saga: бронирование">
  <!-- Шаг 1: Отель -->
  <bpmn:serviceTask id="book_hotel" name="Забронировать отель"
      camunda:delegateExpression="${bookingService.book}">
    <bpmn:boundaryEvent id="comp_hotel" attachedToRef="book_hotel">
      <bpmn:compensateEventDefinition />
    </bpmn:boundaryEvent>
  </bpmn:serviceTask>
  <bpmn:boundaryEvent id="comp_hotel_handler" attachedToRef="book_hotel">
    <bpmn:compensateEventDefinition />
    --->
    <bpmn:serviceTask id="cancel_hotel" name="Отменить отель"
        camunda:delegateExpression="${bookingService.cancel}" />
  </bpmn:boundaryEvent>

  <!-- Шаг 2: Билеты -->
  <bpmn:serviceTask id="buy_tickets" name="Купить билеты">
    <bpmn:boundaryEvent id="comp_tickets" attachedToRef="buy_tickets">
      <bpmn:compensateEventDefinition />
    </bpmn:boundaryEvent>
  </bpmn:serviceTask>
  <bpmn:boundaryEvent id="comp_tickets_handler" attachedToRef="buy_tickets">
    <bpmn:compensateEventDefinition />
    --->
    <bpmn:serviceTask id="cancel_tickets" name="Отменить билеты" />
  </bpmn:boundaryEvent>

  <!-- Шаг 3: Оплата -->
  <bpmn:serviceTask id="charge" name="Списать деньги" />

  <!-- Конец-компенсация при ошибке -->
  <bpmn:endEvent id="comp_end">
    <bpmn:compensateEventDefinition />
  </bpmn:endEvent>
</bpmn:subProcess>

6. Роль системного аналитика при проектировании компенсаций и Saga

6.1. Что должен сделать аналитик

При проектировании процессов с компенсациями аналитик обязан ответить на вопросы:

Вопрос 1: Какие шаги требуют компенсации?

Не каждый шаг нужно откатывать. Некоторые шаги — «без побочных эффектов» (просто проверка данных). Компенсации требуют шаги, которые изменили состояние внешней системы.

Шаг Требует компенсации? Почему
Проверить формат email Нет Не меняет состояние
Забронировать отель Да Отель занял слот — нужно освободить
Отправить email Нет Email уже отправлен — не отменить
Списать деньги Да Деньги ушли — нужно вернуть
Создать запись в БД Да Запись создана — нужно удалить
Запросить внешнюю проверку Нет Это read-only операция

Вопрос 2: Какая компенсация требуется для каждого шага?

Для шага с компенсацией нужно указать компенсирующее действие:

Шаг Компенсация Пример API
bookingService.book(hotelId) bookingService.cancel(bookingId) POST /api/hotels/{id}/cancel
ticketService.purchase(flightId) ticketService.refund(ticketId) POST /api/tickets/{id}/refund
paymentService.charge(amount) paymentService.refund(chargeId) POST /api/payments/{id}/refund
inventoryService.reserve(itemId) inventoryService.unreserve(itemId) POST /api/inventory/{id}/unreserve

Вопрос 3: Что, если сама компенсация упала?

Стратегии:

Стратегия Описание Риск
Retry Повторить компенсацию (с тайм-аутом) Бесконечный цикл, если система мертва
Skip Пропустить, записать в лог Данные остались в неконсистентном состоянии
Manual Создать тикет для поддержки Человеческий фактор, задержка
Dead Letter Queue Сохранить событие ошибки, повторить позже Нужна инфраструктура DLQ

Рекомендация для аналитика: Проектируйте Retry 3 раза с экспоненциальной задержкой. Если все 3 попытки не удались — Manual (тикет поддержке). В TO-BE это выглядит так:

⬭ Service: "Компенсация: отменить отель"
│
⏰ Retry 3 раза (Boundary Timer)
│
◇ XOR [Статус?]
├── [Успех] → Продолжить
└── [Ошибка после 3 retry] → ⬭ User: "Создать тикет для ручной отмены отеля"

Вопрос 4: Идемпотентность компенсаций

Компенсация должна быть идемпотентной — повторный вызов не должен приводить к ошибке. Если мы дважды вызовем cancelBooking(bookingId), второй раз должно вернуть OK (бронь уже отменена).

Аналитик должен указать в ТЗ: «Метод cancelBooking должен быть идемпотентным — повторный вызов с тем же ID возвращает успех, даже если бронь уже отменена».

Вопрос 5: Временные окна (SLA для компенсаций)

У некоторых операций есть «окно отмены»:

  • Авиабилеты: полный возврат за 24 часа до вылета, штраф 50% за 3 часа
  • Отель: бесплатная отмена за 48 часов, штраф 1 сутки — за 24 часа
  • Платёж: полный возврат в течение 24 часов, потом — заявление в банк

Аналитик должен указать: «Если прошло более 24 часов с момента брони билетов — компенсация переходит на ручной процесс (тикет поддержке), так как система не может гарантировать полный возврат».

6.2. Чек-лист аналитика для Saga

  • Определены все шаги, меняющие состояние внешних систем
  • Для каждого такого шага определена компенсация (API, скрипт, ручное действие)
  • Указана идемпотентность каждой компенсации
  • Определена стратегия Retry (сколько раз, интервал, timeout)
  • Определён сценарий «компенсация не удалась» (Manual / DLQ / Skip)
  • Специфицированы временные окна (если отмена невозможна после N часов)
  • Последовательность компенсаций обратная относительно основного процесса
  • Компенсация не вызывает побочных эффектов (не отправляет письма клиенту дважды)
  • В TO-BE добавлен мониторинг: если компенсация упала — алерт в систему
  • Все компенсации протестированы (аналитик проверяет на тестовом стенде)

6.3. Типичные ошибки аналитика при проектировании компенсаций

Ошибка 1: Компенсация не для всех внешних шагов

Забронировали отель и билеты. Компенсацию сделали только для билетов, про отель забыли. В результате: отель остался забронирован, деньги за него не вернулись.

Ошибка 2: Компенсация не идемпотентна

Дважды вызвали отмену брони. Первый раз — OK. Второй раз — ошибка 500, потому что брони уже нет. Процесс упал.

Ошибка 3: Компенсация в том же порядке, а не в обратном

Списание → билеты → отель. Компенсация запустилась: списание → билеты → отель (в том же порядке). Списание уже вернули деньги, а билеты ещё не отменили — пользователь получил деньги, но билеты остались.

Ошибка 4: Отсутствие Retry

Компенсация упала (сетевая ошибка). Статус — Error. Процесс завершился. Оператор не узнал об ошибке. Данные остались несогласованными.

Ошибка 5: Компенсация после тайм-аута (окно закрыто)

Билеты куплены. Процесс упал через 30 часов (время компенсации). А по правилам авиакомпании отмена билета без штрафа — только в течение 24 часов. Аналитик не учёл это — клиент потерял деньги.


7. Дополнительные паттерны BPMN

7.1. Паттерн «Цикл с ограничением»

⬭ User: "Ввести код из SMS"
│
⏰ Retry: max 3 попытки
│
◇ XOR [Код верен?]
├── [Да] → ⬭ Войти в систему → ◉
└── [Нет] → ◇ XOR [Попыток < 3?]
              ├── [Да] → ⬭ User: "Ввести код заново" (назад)
              └── [Нет] → ⬭ Service: "Заблокировать вход на 15 минут" → ◉

Варианты реализации:

  1. Sequence Flow назад — стрелка от шлюза к предыдущей задаче (проще, но риск бесконечного цикла)
  2. Loop Task — встроенный цикл в задаче (свойство loopCharacteristics в XML)
  3. Multi-instance Loop — для параллельного выполнения нескольких экземпляров

7.2. Паттерн «Тайм-аут с уведомлением»

⬭ Service: "Вызвать внешний API"
│
◯⏰ (Boundary Timer, Interrupting, 30 секунд)
│
◇ XOR
├── [API ответил вовремя] → ⬭ Обработать ответ
└── [Тайм-аут] → ⬭ Service: "Повторить запрос" (retry)
                │
                ◯⏰ (Boundary Timer, 30 секунд)
                │
                ◇ XOR [Retry 3?]
                  ├── [Нет] → ⬭ Service: "Повторить"
                  └── [Да] → ⬭ User: "Создать тикет: API недоступен"

7.3. Паттерн «Внешняя подпись документа»

⬭ Service: "Сформировать документ PDF"
→ ⬭ Send: "Отправить документ на подпись (КЭП)"
→ ✦ Event-based Gateway
  ├── ◯✉ (Message: "Документ подписан") → ⬭ Service: "Сохранить в архиве"
  ├── ◯✉ (Message: "Подпись отклонена") → ⬭ User: "Исправить документ"
  └── ◯⏰ (Timer: "3 дня на подпись") → ⬭ Send: "Напомнить о подписи"

8. Вопросы для самопроверки

  1. Что такое Gap-анализ? Зачем моделировать AS-IS, если всё равно строить TO-BE?
  2. Какие 5 шагов включает методика Gap-анализа?
  3. Приведите пример проблемы AS-IS и её решения в TO-BE с указанием BPMN-элемента.
  4. Что такое компенсация в BPMN? Какой элемент её запускает?
  5. В каком порядке выполняются компенсации? Почему?
  6. Что такое Saga-паттерн? Чем он отличается от простой последовательности шагов?
  7. Приведите пример из жизни, где без Saga данные останутся несогласованными.
  8. Какие 5 вопросов должен задать аналитик при проектировании компенсаций?
  9. Что такое идемпотентность компенсации? Почему это важно?
  10. Что произойдёт, если сама компенсация упадёт? Какие стратегии восстановления?
  11. Какие BPMN-элементы используются для эскалации? Приведите пример с двумя уровнями.
  12. В чём разница между «откатом» (rollback) в БД и «компенсацией» в BPMN?

9. Практическое задание

Задание 1. Gap-анализ: процесс найма сотрудника (AS-IS → TO-BE)

Описание AS-IS (текущий процесс найма):

  1. Руководитель пишет HR в мессенджер: «Нужен разработчик»
  2. HR вручную размещает вакансию на hh.ru (через браузер)
  3. Резюме приходят на email HR
  4. HR просматривает резюме, лучшие пересылает руководителю по email
  5. Руководитель читает, отвечает: «Зовём на собеседование» или «Нет»
  6. Если «Да» — HR пишет кандидату письмо с приглашением
  7. Собеседование проводится устно (результат — в заметках руководителя)
  8. Если прошёл — HR готовит бумажный договор
  9. Договор подписывается лично (Кандидат → HR → Руководитель → HR)
  10. Сотрудник приступает к работе

Задание:

  1. Постройте BPMN-диаграмму AS-IS. Минимум 4 Lane: Руководитель, HR, Кандидат, Система.
  2. Проведите Gap-анализ: выявите минимум 5 проблем, запишите в таблицу (Проблема → Риск → TO-BE решение).
  3. Спроектируйте BPMN-диаграмму TO-BE с решениями проблем. Используйте:
    • Service Task — интеграция с hh.ru (авто-публикация вакансии)
    • User Task — HR проверяет резюме в ATS, не в email
    • Event-based Gateway — руководитель может ответить «Зовём» / «Нет» / тайм-аут 3 дня
    • Boundary Timer (Non-Interrupting) — напоминание через 2 дня
    • Boundary Timer (Interrupting) — эскалация через 5 дней
    • Compensation — если договор не подписан за 7 дней — откатить все шаги
    • Manual TaskUser Task (в TO-BE электронная подпись)

Задание 2. Saga: аренда автомобиля в каршеринге

Ситуация: Пользователь бронирует автомобиль в каршеринге. Процесс состоит из 3 шагов:

  1. Блокировка счёта — Payment Service блокирует (hold) сумму на карте пользователя (например, 3000 ₽ — депозит)
  2. Резерв автомобиля — Car Service резервирует конкретный автомобиль за пользователем (15 минут)
  3. Открытие доступа — Telematics Service активирует доступ к автомобилю (отключает иммобилайзер)

Проблема: Если на шаге 3 произошла ошибка (телематический модуль не отвечает), нужно откатить шаги 1 и 2.

Задание:

  1. Нарисуйте BPMN-диаграмму Saga с компенсациями.
  2. Для каждого шага укажите компенсирующее действие:
    • Шаг 1 (блокировка счёта) → компенсация: разблокировать счёт (release hold)
    • Шаг 2 (резерв авто) → компенсация: отменить резерв (доступно другим)
    • Шаг 3 (телематика) — ошибка → запуск компенсаций в обратном порядке
  3. Что произойдёт, если компенсация шага 2 (отмена резерва) не удалась? Опишите сценарий в BPMN (retry → manual).

Задание 3. Анализ: спроектируйте TЗ для аналитика

Вам нужно составить Техническое Задание (ТЗ) для разработчика на реализацию Saga из Примера 2 (Каршеринг).

Заполните таблицу для каждого Service Task (шага и компенсации):

Шаг: "Заблокировать счёт"
Компенсация: "Разблокировать счёт"
HTTP метод: POST
URL: https://payment.internal/api/v1/holds
Компенсация URL: https://payment.internal/api/v1/holds/{holdId}/release
Timeout: 10 секунд
Retry: 3, с интервалом 2 сек
Идемпотентность: Да (если holdId уже released  200 OK)
Требования к безопасности: Internal network only, mTLS

Проделайте то же для шага «Резерв автомобиля» и его компенсации.

Задание 4. Письменный анализ (рефлексия)

Ответьте на вопросы (3–5 предложений каждый):

  1. «Почему компенсации выполняются в обратном порядке? Приведите метафору из жизни (не про BPMN).»

  2. «Какая, на ваш взгляд, самая опасная ошибка при проектировании Saga (см. раздел 6.3)? Почему? Как её избежать?»

  3. «Зачем системному аналитику понимать, как работают компенсации? Ведь их реализует разработчик.»

  4. «Что будет, если не проводить Gap-анализ, а сразу начать проектировать TO-BE? Приведите пример из своей практики (или воображаемой).»


10. Дополнительные материалы

  • Книга: Bruce Silver — «BPMN Method and Style: A Levels-based Methodology for BPM Process Modeling» (лучшая по стилю моделирования, включая компенсации)
  • Книга: Chris Richardson — «Microservices Patterns» (глава «Saga Pattern» — подробно про распределённые транзакции)
  • Паттерны: Workflow Patterns — workflowpatterns.com — 43 паттерна для BPMN, включая компенсации
  • Статья: Camunda Blog — «Compensation in BPMN: When and How to Use It» (практические примеры)
  • Видео: «BPMN Saga Pattern with Compensation» — CamundaCon presentation (YouTube, 30 минут)
  • Стандарт: OMG BPMN 2.0, раздел «Compensation Handling» (глава 13.3.6)
  • Инструмент: Camunda Modeler — откройте пример «Order Processing» (File → Open Examples) — там есть компенсация
  • Практика: BPMN Sketcher — bpmn-sketch.mini — быстрый онлайн-инструмент для набросков BPMN (без установки)
  • Метрики: Как измерять процесс: Cycle Time, SLA Compliance, Rework Rate — изучите для Gap Analysis

Следующий модуль: 06 — Моделирование данных и SQL

📚 Материалы модуля

🖼️ Схема и инфографика

🎬 Видео-лекция

🎬 BPMN 2

📄 Дополнительные материалы (PDF)

📄Исполняемый чертеж BPMN 2.0
Скачать
Спросить ИИ