Урок 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:
- Сотруднику нужно купить товар (например, ноутбук)
- Сотрудник находит товар в интернет-магазине, копирует ссылку
- Сотрудник пишет письмо руководителю: «Купить ноутбук за 75 000 ₽? Ссылка...»
- Руководитель читает письмо, отвечает «Ок, давай» или «Нет, не нужно»
- Если ответ «Нет» — процесс завершён
- Если ответ «Да» — сотрудник проверяет сумму
- Если сумма ≤ 50 000 ₽ — сотрудник печатает заявку, несёт в бухгалтерию
- Если сумма > 50 000 ₽ — сотрудник идёт к финансовому директору лично
- Финдиректор (если нужно) смотрит бюджет, говорит «Да/Нет»
- Если финдиректор согласен — сотрудник печатает заявку, несёт в бухгалтерию
- Бухгалтер получает бумажную заявку, вручную вводит данные в 1С
- Заявка создана в 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:
- Внедряется единая система (CRM с BPMN-движком)
- Заявки создаются в веб-форме, не в email
- Проверка бюджета — автоматическая (DMN-таблица) или электронное согласование
- 1С интегрируется через API — Service Task
- На каждый шаг — SLA с таймаутами и эскалацией
- Решения фиксируются с комментариями
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. Бизнес-контекст
Туристический сервис. Пользователь бронирует:
- Отель — брон. номера
- Авиабилеты — покупка билетов
- Списание денег — оплата с карты
Проблема: Это распределённая транзакция через 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 минут" → ◉
Варианты реализации:
- Sequence Flow назад — стрелка от шлюза к предыдущей задаче (проще, но риск бесконечного цикла)
- Loop Task — встроенный цикл в задаче (свойство
loopCharacteristicsв XML) - 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. Вопросы для самопроверки
- Что такое Gap-анализ? Зачем моделировать AS-IS, если всё равно строить TO-BE?
- Какие 5 шагов включает методика Gap-анализа?
- Приведите пример проблемы AS-IS и её решения в TO-BE с указанием BPMN-элемента.
- Что такое компенсация в BPMN? Какой элемент её запускает?
- В каком порядке выполняются компенсации? Почему?
- Что такое Saga-паттерн? Чем он отличается от простой последовательности шагов?
- Приведите пример из жизни, где без Saga данные останутся несогласованными.
- Какие 5 вопросов должен задать аналитик при проектировании компенсаций?
- Что такое идемпотентность компенсации? Почему это важно?
- Что произойдёт, если сама компенсация упадёт? Какие стратегии восстановления?
- Какие BPMN-элементы используются для эскалации? Приведите пример с двумя уровнями.
- В чём разница между «откатом» (rollback) в БД и «компенсацией» в BPMN?
9. Практическое задание
Задание 1. Gap-анализ: процесс найма сотрудника (AS-IS → TO-BE)
Описание AS-IS (текущий процесс найма):
- Руководитель пишет HR в мессенджер: «Нужен разработчик»
- HR вручную размещает вакансию на hh.ru (через браузер)
- Резюме приходят на email HR
- HR просматривает резюме, лучшие пересылает руководителю по email
- Руководитель читает, отвечает: «Зовём на собеседование» или «Нет»
- Если «Да» — HR пишет кандидату письмо с приглашением
- Собеседование проводится устно (результат — в заметках руководителя)
- Если прошёл — HR готовит бумажный договор
- Договор подписывается лично (Кандидат → HR → Руководитель → HR)
- Сотрудник приступает к работе
Задание:
- Постройте BPMN-диаграмму AS-IS. Минимум 4 Lane: Руководитель, HR, Кандидат, Система.
- Проведите Gap-анализ: выявите минимум 5 проблем, запишите в таблицу (Проблема → Риск → TO-BE решение).
- Спроектируйте 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 Task → User Task (в TO-BE электронная подпись)
Задание 2. Saga: аренда автомобиля в каршеринге
Ситуация: Пользователь бронирует автомобиль в каршеринге. Процесс состоит из 3 шагов:
- Блокировка счёта — Payment Service блокирует (hold) сумму на карте пользователя (например, 3000 ₽ — депозит)
- Резерв автомобиля — Car Service резервирует конкретный автомобиль за пользователем (15 минут)
- Открытие доступа — Telematics Service активирует доступ к автомобилю (отключает иммобилайзер)
Проблема: Если на шаге 3 произошла ошибка (телематический модуль не отвечает), нужно откатить шаги 1 и 2.
Задание:
- Нарисуйте BPMN-диаграмму Saga с компенсациями.
- Для каждого шага укажите компенсирующее действие:
- Шаг 1 (блокировка счёта) → компенсация: разблокировать счёт (release hold)
- Шаг 2 (резерв авто) → компенсация: отменить резерв (доступно другим)
- Шаг 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 предложений каждый):
-
«Почему компенсации выполняются в обратном порядке? Приведите метафору из жизни (не про BPMN).»
-
«Какая, на ваш взгляд, самая опасная ошибка при проектировании Saga (см. раздел 6.3)? Почему? Как её избежать?»
-
«Зачем системному аналитику понимать, как работают компенсации? Ведь их реализует разработчик.»
-
«Что будет, если не проводить 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