Урок 05.02: Элементы процесса (события, задачи, шлюзы)
Цель урока
Детально разобрать три основные категории Flow Objects BPMN 2.0: события, задачи и шлюзы. После этого урока вы сможете:
- Различать ~50 комбинаций событий и выбирать нужную
- Писать техническое задание на Service Task с JSON-контрактами
- Правильно использовать Event-based Gateway (только с Intermediate Catch Events!)
- Выбирать между Interrupting и Non-Interrupting Boundary Event
- Проектировать процессы с SLA, эскалацией, тайм-аутами и компенсациями
1. События (Events) — полный справочник
События — самая богатая категория элементов BPMN. В BPMN 2.0 определено ~50 комбинаций: 3 позиции (Start / Intermediate / End) × ~16 маркеров × 2 режима (Interrupting / Non-Interrupting для Intermediate Boundary).
1.1. Классификация по положению
Событие — это круг. Положение круга определяет, когда в процессе оно происходит:
| Тип | Нотация | Расположение | Всегда | Описание |
|---|---|---|---|---|
| Start Event | ○ (одинарная тонкая линия) | Начало процесса | Catching | Ловит событие, которое запускает процесс |
| Intermediate Event | ◯ (двойная линия) | В середине потока или на границе задачи | Catching или Throwing | Ждёт или инициирует событие в ходе процесса |
| End Event | ◉ (одинарная жирная линия) | Конец процесса | Throwing | Завершает процесс и бросает событие-результат |
1.2. Полная матрица событий (все ~50 комбинаций)
| Маркер | Start (Catch) | Intermediate Catch | Intermediate Throw | End (Throw) | Boundary Interrupting | Boundary Non-Interrupting |
|---|---|---|---|---|---|---|
| None (пусто) | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| Message ✉ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Timer ⏰ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |
| Error ❌ | ❌ | ✅ (редко) | ✅ | ✅ | ✅ | ❌ |
| Escalation ⚠ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| Signal 📌 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Link 🔗 | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| Compensation ⚖ | ❌ | ✅ (только в подпроцессе) | ✅ | ✅ | ❌ | ❌ |
| Conditional ❓ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |
| Terminate 💀 | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
1.3. Start Event — подробно по каждому типу
| Start Event | Маркер | Как инициируется | XML в Camunda | Пример |
|---|---|---|---|---|
| None | ○ (пустой) | Вручную через Cockpit / REST API | <startEvent id="start" /> |
Запуск тестового процесса из админки |
| Message | ○✉ | Получение сообщения (HTTP, Kafka, JMS) | <startEvent id="start"><messageEventDefinition /></startEvent> |
Поступил заказ из интернет-магазина |
| Timer | ○⏰ | По расписанию (cron / duration) | <startEvent id="start"><timerEventDefinition><timeCycle>0 0 8 * * ?</timeCycle></timerEventDefinition></startEvent> |
Каждый день в 8:00 формировать отчёт |
| Signal | ○📌 | Получение широковещательного сигнала | <startEvent id="start"><signalEventDefinition signalRef="signal_new_quarter" /></startEvent> |
Сигнал «Начат новый квартал» |
| Conditional | ○❓ | Выполнение условия (изменение данных) | <startEvent id="start"><conditionalEventDefinition><condition>${stock < 10}</condition></conditionalEventDefinition></startEvent> |
Остаток товара меньше 10 штук |
Важнейшее правило для аналитика: Выбор Start Event определяет, как процесс запускается в реальности.
❌ Ошибка: Start Event (None) для процесса, который стартует по сообщению из CRM.
Разработчик не поймёт, откуда брать входные данные.
✅ Правильно: Start Event (Message) ✉ — означает «процесс запускается, когда пришло сообщение».
В XML будет messageEventDefinition, и разработчик знает, что нужен Message-контракт.
1.4. Intermediate Events — Catching (ловящие) vs Throwing (бросающие)
Catching Event (Ловящее событие): Процесс останавливается и ждёт, пока событие не произойдёт.
Throwing Event (Бросающее событие): Процесс инициирует событие (не останавливаясь) и продолжается.
Визуальное отличие:
Catching: ◯✉ — двойная линия (полый круг), маркер внутри
Throwing: ◉✉ — если End Event; ◯✉ с «вылетающим» маркером — Intermediate Throw
Но в стандартной нотации Intermediate Catch и Intermediate Throw выглядят одинаково (оба — двойная линия). Различие — в контексте: Catch ожидает, Throw отправляет.
Пример Catching: Пример Throwing:
⬭ Отправить запрос ⬭ Сформировать отчёт
│ │
◯⏰ (Catch) ◯✉ (Throw)
│ │
(ждёт 30 секунд) (отправляет email)
│ │
⬭ Обработать ответ ⬭ Продолжить процесс
Таблица: когда Catching, когда Throwing
| Тип | Маркер | Catching? | Throwing? | Пример Catching | Пример Throwing |
|---|---|---|---|---|---|
| Message | ✉ | ✅ | ✅ | Ждать ответ от API | Отправить уведомление |
| Timer | ⏰ | ✅ | ❌ | Ждать 30 минут | — |
| Error | ❌ | ✅ (редко) | ✅ | Ловить ошибку из подпроцесса | Выбросить ошибку |
| Signal | 📌 | ✅ | ✅ | Ждать сигнал «Отмена» | Послать сигнал «Готово» |
| Link | 🔗 | ✅ | ✅ | Ждать продолжения с другой страницы | Перейти на другую страницу |
| Compensation | ⚖ | ✅ | ✅ | Ждать компенсации | Запустить компенсацию |
1.5. End Event — типы завершения
| End Event | Маркер | Что происходит | XML |
|---|---|---|---|
| None | ◉ (пустой) | Процесс завершается без специального результата | <endEvent id="end" /> |
| Message | ◉✉ | Отправляется сообщение (результат процесса) | <endEvent id="end"><messageEventDefinition /></endEvent> |
| Error | ◉❌ | Процесс завершается с ошибкой — ищет Error Boundary | <endEvent id="end"><errorEventDefinition errorRef="err_not_found" /></endEvent> |
| Terminate | ◉● (жирный) | Мгновенно завершает ВСЕ потоки процесса | <endEvent id="end"><terminateEventDefinition /></endEvent> |
| Compensation | ◉⚖ | Завершает процесс и запускает компенсацию | <endEvent id="end"><compensateEventDefinition /></endEvent> |
| Escalation | ◉⚠ | Завершает с эскалацией | <endEvent id="end"><escalationEventDefinition /></endEvent> |
| Signal | ◉📌 | Рассылает сигнал при завершении | <endEvent id="end"><signalEventDefinition signalRef="sig_name" /></endEvent> |
Terminate End — когда использовать?
◉● Terminate End убивает ВСЕ параллельные потоки. ◉ None завершает только текущий.
Пример:
┌── ⬭ Сохранить в БД ── ⬭ Отправить email ── ◉ End
│
Start ────┤
│
└── ⬭ Проверить ── ◇ XOR [ошибка?]
├── [Да] ── ⬭ Записать ошибку ── ◉● Terminate
└── [Нет] ── ◉ End
Если сработал Terminate — все потоки (включая «Сохранить в БД → Отправить email») прекращаются. Если обычный End — только поток ошибки.
Когда нужен Terminate:
- Обнаружена фатальная ошибка (нет смысла продолжать другие ветки)
- Пользователь отменил операцию
- Нарушены условия договора
Когда НЕ нужен Terminate: почти во всех остальных случаях. Terminate — грубый инструмент.
2. Задачи (Tasks) — детальный разбор с примерами
2.1. User Task — задача для человека
Назначение: Шаг процесса, который выполняет человек через интерфейс (веб-форму, мобильное приложение).
Визуал: Скруглённый прямоугольник с иконкой человечка 🧑
Характеристики:
- Человек видит задачу в своём Task List (Camunda Tasklist, пользовательский UI)
- Может содержать форму для заполнения (Camunda Forms, React Forms)
- Имеет assignee (конкретный пользователь) или candidate group (роль)
- Имеет SLA (срок выполнения) — задаётся через Boundary Timer
Пример XML:
<userTask id="approve_request" name="Согласовать заявку"
camunda:assignee="${manager}"
camunda:dueDate="2025-06-01T18:00:00">
<bpmn:extensionElements>
<camunda:formData>
<camunda:formField id="decision" label="Решение" type="string">
<camunda:constraint name="required" />
</camunda:formField>
<camunda:formField id="comment" label="Комментарий" type="string" />
</camunda:formData>
</bpmn:extensionElements>
</userTask>
Вопросы аналитика к User Task:
| Вопрос | Зачем |
|---|---|
| Кто выполняет? (роль / конкретный человек) | Задать assignee или candidateGroups |
| Какую форму заполняет? | Спроектировать форму (поля + валидация) |
| Сколько времени даётся? (SLA) | Boundary Timer → эскалация |
| Что делать, если не выполнил? | Non-Interrupting напоминание → Interrupting эскалация |
| Какие данные вводит? | Input/output переменные процесса |
2.2. Service Task — автоматическая операция (с детальным ТЗ)
Назначение: Шаг процесса, который выполняется системой (API, скрипт, вызов микросервиса).
Визуал: Скруглённый прямоугольник с иконкой шестерёнки ⚙
Критически важно для аналитика: Service Task — это точка интеграции. Аналитик должен специфицировать контракт: какие данные входят, какие выходят, что при ошибке.
Техническое задание (ТЗ) для Service Task — шаблон
Для каждого Service Task аналитик должен заполнить таблицу контракта:
Service Task: "processPayment"
Описание: Вызов платёжного шлюза Stripe для списания средств
Тип реализации: External Task (REST)
HTTP-метод: POST
URL: https://api.stripe.com/v1/charges
Тайм-аут: 30 секунд
Retry: 3 попытки с интервалом 5 секунд
Входные данные (Input):
| Поле | Тип | Источник | Описание |
|---|---|---|---|
paymentToken |
String | Из формы пользователя | Токен карты, полученный от Stripe.js |
amount |
Decimal | Из переменной order.total |
Сумма в минимальных единицах валюты |
currency |
String (3) | Константа процесса | "RUB" |
orderId |
UUID | Переменная процесса order.id |
Внутренний ID для сопоставления |
description |
String | Шаблон | "Оплата заказа №" + orderId |
Выходные данные (Output):
| Поле | Тип | Куда сохраняется | Описание |
|---|---|---|---|
chargeId |
String | payment.chargeId |
ID транзакции в Stripe |
status |
String | payment.status |
"succeeded" / "failed" |
paidAt |
DateTime | payment.paidAt |
Время списания |
gatewayResponse |
JSON | payment.gatewayResponse |
Полный ответ от шлюза (для отладки) |
JSON-запрос (пример):
{
"amount": 150000,
"currency": "rub",
"source": "tok_visa",
"description": "Оплата заказа № 12345",
"metadata": {
"order_id": "12345",
"customer_id": "user_67890"
}
}
JSON-ответ (успех):
{
"id": "ch_3Lm9n8V1e2U3o4C5",
"object": "charge",
"amount": 150000,
"currency": "rub",
"status": "succeeded",
"paid": true,
"payment_method": "card_1Ab2Cd3Ef4Gh5Ij6",
"metadata": {
"order_id": "12345"
},
"created": 1717094400
}
JSON-ответ (ошибка):
{
"error": {
"type": "card_error",
"code": "insufficient_funds",
"message": "Недостаточно средств на карте"
}
}
Обработка ошибок:
| Код ошибки | Действие в BPMN |
|---|---|
card_error — declined |
Вернуть ошибку бизнес-логики → Exclusive Gateway: «Платеж не прошёл» |
validation_error — неверные данные |
Service Task error → Boundary Error → эскалация разработчику |
| Тайм-аут (30 сек) | Boundary Timer → Retry (через цикл) |
| HTTP 500 (шлюз недоступен) | Retry 3 раза, потом эскалация |
Все способы реализации Service Task в Camunda
| Тип реализации | Описание | Когда использовать | Пример |
|---|---|---|---|
| Delegate Expression | Вызов Java-класса (в том же JVM) | Простая логика внутри Camunda | ${inventoryService.checkStock} |
| External Task (REST) | HTTP-вызов к внешнему микросервису | Микросервисная архитектура | HTTP POST /api/payment |
| Connector (REST) | Встроенный REST-коннектор | Быстрая интеграция без кода | Camunda Connector для HTTP |
| Connector (SOAP) | SOAP-вызов | Интеграция с legacy-системами | SOAP к 1С |
| Expression | Вычисление выражения | Простая арифметика или конкатенация | ${price * quantity} |
| Script | JavaScript / Groovy / Python | Небольшие скрипты внутри Camunda | Проверка формата email |
Чек-лист для аналитика: Service Task
Перед тем как отдать Service Task разработчику, проверьте:
- Указан тип реализации (Delegate / External / Connector)
- Специфицирован входной JSON (все поля, типы, источники данных)
- Специфицирован выходной JSON (куда сохраняются поля в переменные процесса)
- Указан URL эндпоинта (если External Task)
- Указан HTTP-метод (GET / POST / PUT / PATCH / DELETE)
- Указан тайм-аут (сколько система ждёт ответ)
- Указана политика Retry (сколько раз повторять, с каким интервалом)
- Описаны обработчики ошибок (что делать при 4xx, 5xx, тайм-ауте)
2.3. Send Task — отправка сообщения
Назначение: Отправить сообщение внешней системе / другому пулу.
Визуал: Скруглённый прямоугольник с конвертом ✉ (залитый конверт — Send, полый — Receive).
Отличие от Service Task: Send Task только отправляет и не ждёт ответа. Service Task — отправляет И ждёт ответ (синхронно).
Когда Send Task:
- Отправить email (но обычно это Service Task с вызовом email-сервиса)
- Отправить событие в Kafka (но обычно это Service Task)
- Отправить данные в другой пул (если другой процесс должен стартовать)
Когда НЕ Send Task:
- Нужно отправить запрос И получить ответ → Service Task
- Нужно ждать сообщение → Receive Task
2.4. Receive Task — ожидание сообщения
Назначение: Заблокировать процесс до получения сообщения.
Визуал: Скруглённый прямоугольник с полым конвертом.
Отличие от Intermediate Catch Message Event:
| Аспект | Receive Task | Intermediate Catch Message Event |
|---|---|---|
| Визуал | Прямоугольник с конвертом | Круг с конвертом |
| Может иметь форму? | Да (Camunda Form) | Нет |
| Гибкость | Меньше (только сообщение) | Больше (можно любой тип) |
| Рекомендация | Используйте Intermediate Catch Event | Стандартный выбор |
На практике: Intermediate Catch Message Event — почти всегда лучший выбор. Receive Task — для совместимости со старыми моделями.
2.5. Business Rule Task — бизнес-правила (DMN)
Назначение: Выполнить таблицу правил (Decision Model and Notation — DMN).
Визуал: Скруглённый прямоугольник с иконкой таблицы 📋
Что даёт DMN:
- Правила можно менять без остановки процесса
- Правила понятны бизнес-аналитику (таблица, не код)
- Каждое правило — строка в Decision Table
Пример DMN-таблицы для расчёта скидки:
| Сумма заказа | Кол-во товаров | Статус клиента | Результат (скидка) |
|---|---|---|---|
< 1000 |
— | — | 0% |
≥ 1000 и < 5000 |
— | NEW |
3% |
≥ 1000 и < 5000 |
— | REGULAR или VIP |
5% |
≥ 5000 |
— | NEW |
7% |
≥ 5000 |
— | REGULAR |
10% |
≥ 5000 |
≥ 5 |
VIP |
15% |
Вопросы аналитика к Business Rule Task:
- Какие входные данные нужны для принятия решения?
- Какие выходные данные ожидаются?
- Кто будет редактировать таблицу правил? (бизнес-пользователь?)
- Как часто меняются правила?
2.6. Script Task — выполнить скрипт
Назначение: Выполнить небольшой скрипт внутри процесса.
Визуал: Скруглённый прямоугольник с иконкой свитка 📜
Когда Script Task удобен:
- Простая конвертация данных
- Генерация ID
- Форматирование строки
- Вызов простого расчёта (не подходящего под DMN)
Когда Script Task НЕЛЬЗЯ:
- Сложная бизнес-логика (выносите в Service Task)
- Работа с внешними API (Service Task с External Task)
- Длительные операции (Script Task блокирует поток)
Пример (JavaScript в Camunda):
// Формирование приветствия
var firstName = execution.getVariable("firstName");
var lastName = execution.getVariable("lastName");
var greeting = "Уважаемый(ая), " + firstName + " " + lastName + "!";
execution.setVariable("greeting", greeting);
2.7. Manual Task — ручная операция
Назначение: Шаг, выполняемый человеком без использования ИТ-системы.
Визуал: Скруглённый прямоугольник с иконкой руки 🖐
Примеры:
- «Подписать документ лично» (бумага)
- «Передать накладную курьеру»
- «Проверить качество продукции визуально»
Отличие от User Task:
- User Task — человек работает через систему (заполняет форму)
- Manual Task — человек делает что-то вне системы (процесс просто ждёт)
2.8. Call Activity — вызов подпроцесса
Назначение: Вызов другого BPMN-процесса (переиспользуемый подпроцесс).
Визуал: Скруглённый прямоугольник с толстой границей и знаком +
Отличие от Embedded Subprocess:
- Embedded Subprocess — внутри той же диаграммы (разворачивается по клику)
- Call Activity — ссылка на отдельный BPMN-файл (можно переиспользовать в разных процессах)
Для аналитика: Если один и тот же блок («Проверить кредитную историю» / «Отправить уведомление») используется в нескольких процессах — выносите в Call Activity.
3. ⚠️ Event-based Gateway — критически правильный синтаксис
3.1. Самая частая ошибка с Event-based Gateway
НЕПРАВИЛЬНО (синтаксическая ошибка BPMN):
❌
⬭ Отправить предложение
│
✦ (Event-based)
├── ◇ XOR "Клиент согласился?" ← ОШИБКА! После Event-based Gateway
└── ◯⏰ Timer "7 дней прошло" ← ДОЛЖНЫ быть только Intermediate Catch Events
Почему это ошибка: Event-based Gateway выбирает по событию. XOR выбирает по данным. Они не могут стоять после Event-based Gateway — шлюз уже «выбрал» по событию. После Event-based Gateway может быть только Intermediate Catch Event (Message, Timer, Signal).
3.2. ПРАВИЛЬНЫЙ синтаксис
✅
⬭ Отправить предложение
│
✦ (Event-based Gateway)
├── ◯✉ (Intermediate Catch Message: "Клиент согласился")
│ │
│ ▼
│ ⬭ Создать договор
│
├── ◯✉ (Intermediate Catch Message: "Клиент отказался")
│ │
│ ▼
│ ⬭ Закрыть заявку
│
└── ◯⏰ (Intermediate Catch Timer: "7 дней прошло")
│
▼
⬭ Отправить напоминание
За Event-based Gateway ВСЕГДА идут только Intermediate Catch Events. Шлюз ждёт первое из них. Какое событие наступило — по тому пути процесс идёт.
3.3. Как работает Event-based Gateway (пошагово)
Шаг 1: Процесс подходит к Event-based Gateway
Шаг 2: Все исходящие Intermediate Catch Events активируются одновременно
Шаг 3: Процесс ЗАМИРАЕТ и ждёт первое наступившее событие
Шаг 4: Как только событие наступило — процесс идёт по этой ветке
Шаг 5: Остальные промежуточные события деактивируются (отменяются)
Аналогия: Вы ожидаете в аэропорту рейс. У вас три варианта:
- ✉ — объявили посадку (Message)
- ⏰ — через 30 минут закончилась регистрация (Timer)
- ✉ — пришло SMS, что рейс отменили (Message)
Что наступит первым — по тому пути вы идёте.
3.4. Какие события могут следовать за Event-based Gateway?
| Тип Intermediate Catch Event | Разрешён после Event-based? | Пример |
|---|---|---|
| Message ✉ | ✅ Да | Ожидание ответа от клиента |
| Timer ⏰ | ✅ Да | Тайм-аут ожидания |
| Signal 📌 | ✅ Да | Ожидание сигнала от системы |
| Conditional ❓ | ✅ Да (редко) | Изменение данных |
| Error ❌ | ❌ Нет | Ошибка — не событие выбора |
| Escalation ⚠ | ❌ Нет | Эскалация — не событие выбора |
| Link 🔗 | ❌ Нет | Ссылка — не событие выбора |
3.5. Event-based Gateway vs Exclusive Gateway
| Аспект | Exclusive (XOR) | Event-based |
|---|---|---|
| Как выбирает? | По данным (условие на Sequence Flow) | По событию (кто первый наступил) |
| Что после шлюза? | Любые элементы (Task, Gateway, Event) | Только Intermediate Catch Events |
| Процесс блокируется? | Нет (мгновенный выбор) | Да (ждёт события) |
| Default flow? | Да (если ни одно условие не истинно) | Нет (должен быть таймер, иначе вечное ожидание) |
| Аналог в коде | if-else |
Promise.race() |
3.6. Event-based Gateway — пример с 3 ветками (правильный)
Кейс: После отправки предложения клиенту процесс ждёт:
⬭ Отправить коммерческое предложение
│
✦ (Event-based Gateway)
│
├── ◯✉ (Message: "Клиент принял предложение")
│ │
│ ▼
│ ⬭ Сформировать договор → ◉ End
│
├── ◯✉ (Message: "Клиент отправил контраргументы")
│ │
│ ▼
│ ⬭ Пересмотреть условия → ... (возврат к предложению)
│
└── ◯⏰ (Timer: "14 дней без ответа")
│
▼
⬭ Пометить предложение как "просрочено" → ◉ End
Критическое условие: Одна из веток Event-based Gateway должна иметь Timer. Иначе если ни одно сообщение не придёт — процесс зависнет навсегда. Timer — это «страховка от вечного ожидания».
3.7. Event-based Gateway — Merge
Event-based Gateway не требует Merge — каждая ветка идёт своим путём. Если ветки должны сойтись — используйте обычный Exclusive Gateway (XOR) после того, как ветка завершила свой подпроцесс.
✦ Event-based
├── ◯✉ → ⬭ A → ⬭ B ──┐
│ ├── ◇ XOR → ⬭ Общий шаг
└── ◯⏰ → ⬭ C ─────────┘
4. Граничные события (Boundary Events) — полный разбор
4.1. Что такое Boundary Event?
Boundary Event — событие, прикреплённое к границе задачи или подпроцесса. Срабатывает во время выполнения задачи, не打断 (не прерывая? наоборот, может прерывать или не прерывать) задачу.
Визуал: Маленький кружок на границе прямоугольника задачи.
⬭ Задача (длительная)
│
◯⏰ (Boundary Event)
│
▼
⬭ Альтернативный путь
4.2. Два режима: Interrupting vs Non-Interrupting
Это самое важное различие Boundary Events.
| Режим | Граница | Поведение | Визуал |
|---|---|---|---|
| Interrupting | Сплошная линия | Событие сработало → задача прервана (немедленно). Поток уходит на альтернативный путь. | ◯ (сплошной круг) |
| Non-Interrupting | Пунктирная линия | Событие сработало → задача продолжается параллельно. Запускается дополнительный поток. | ◯ (пунктирный круг) |
4.3. Пошаговый разбор: пример с SLA и напоминаниями сотруднику
Кейс: Процесс согласования заявки на закупку. У руководителя есть 48 часов на согласование. Нужно:
- Через 24 часа — напомнить (не прерывая задачу)
- Через 48 часов — эскалировать вышестоящему руководителю (прерывая задачу)
Диаграмма:
⬭ User Task: "Согласовать заявку" (48 часов на выполнение)
│
├──── ◯⏰ (Non-Interrupting, 24ч)
│ │
│ ▼
│ ⬭ Send: "Отправить напоминание"
│
└──── ◯⏰ (Interrupting, 48ч)
│
▼
⬭ Service: "Эскалировать руководителю"
Шаг за шагом:
t=0: ⬭ User Task "Согласовать заявку" — назначена руководителю.
Оба Boundary Timer'а запущены.
t=24ч: ◯⏰ Non-Interrupting (24ч) сработал.
User Task НЕ ПРЕРЫВАЕТСЯ — руководитель всё ещё может согласовать.
Запускается параллельный поток: ⬭ "Отправить напоминание".
Руководитель получает email: «Заявка ожидает согласования уже 24ч».
t=36ч: Руководитель заходит в систему, видит задачу и напоминание.
Он может согласовать — тогда задача завершается, оба Boundary'я отменяются.
t=48ч: User Task всё ещё не завершена.
◯⏰ Interrupting (48ч) сработал — User Task ПРЕРВАНА.
Задача снимается с руководителя.
Процесс идёт по пути эскалации:
⬭ Service "Эскалировать руководителю" — создаётся новая задача на вышестоящего.
Почему это важно?
| Если бы оба были Interrupting | Если бы оба были Non-Interrupting |
|---|---|
| В 24ч задача прервалась бы, и руководитель не смог бы её согласовать после 24ч | В 48ч задача продолжилась бы, эскалация НЕ прервала бы задачу — руководитель мог бы согласовать и после 48ч |
Разница между 24ч и 48ч принципиальна:
- 24ч: «Мы напоминаем, но не заставляем» → Non-Interrupting
- 48ч: «Время вышло, передаём другому» → Interrupting
4.4. Таблица: когда Interrupting, когда Non-Interrupting
| Ситуация | Какой Boundary? | Почему |
|---|---|---|
| Напоминание через N часов | Non-Interrupting | Не прерываем задачу, только напоминаем |
| Эскалация по истечении SLA | Interrupting | Время вышло — передаём другому |
| Ошибка в Service Task | Interrupting | Ошибка — надо прервать и обработать |
| Обновление статуса (пошёл новый регламент) | Non-Interrupting | Параллельно уведомить, но не прерывать |
| Отмена заказа (клинт передумал) | Interrupting | Дальнейшее выполнение не имеет смысла |
| Параллельная проверка | Non-Interrupting | Запустить проверку, не останавливая основную задачу |
4.5. Все типы Boundary Events
| Тип | Маркер | Interrupting | Non-Interrupting | Пример |
|---|---|---|---|---|
| Timer | ⏰ | ✅ | ✅ | SLA, напоминание, тайм-аут |
| Message | ✉ | ✅ | ✅ | Получен отказ → прервать; получено уточнение → не прерывать |
| Error | ❌ | ✅ | ❌ | Ошибка в Service Task |
| Signal | 📌 | ✅ | ✅ | Аврал → прервать; системное уведомление → параллельно |
| Escalation | ⚠ | ✅ | ✅ | Нужен эксперт → прервать; нужно мнение → параллельно |
| Conditional | ❓ | ✅ | ✅ | Данные изменились → прервать; данные изменились → уведомить |
Error Boundary — единственный, который бывает только Interrupting. Non-Interrupting Error Boundary не существует (ошибка всегда прерывает).
4.6. Error Boundary Event — обработка ошибок в Service Task
Кейс: Service Task вызывает внешний API. API вернул HTTP 500. Нужно не ронять процесс, а обработать ошибку.
⬭ Service Task: "Вызвать платёжный шлюз"
│
◯❌ (Error Boundary, Interrupting)
│
▼
◇ XOR
├── [retryCount < 3] → ⬭ Service Task (повтор)
└── [retryCount >= 3] → ⬭ Send: "Уведомить администратора"
Как работает:
- Service Task выбрасывает ошибку (BPMN Error)
- Error Boundary «ловит» её на границе задачи
- Service Task прерывается
- Процесс идёт по пути обработки ошибки
Важно: Error Boundary ловит только BPMN Error, брошенные внутри Service Task. HTTP 500 сам по себе не является BPMN Error — разработчик должен преобразовать HTTP-ошибку в BPMN Error.
5. Подпроцессы (Subprocesses) — детально
5.1. Embedded Subprocess — встроенный подпроцесс
Визуал: Скруглённый прямоугольник с толстой границей и знаком +. Можно развернуть (+ → −) и увидеть содержимое.
Когда использовать:
- Группировка логически связанных шагов
- Локальная обработка ошибок (Error Boundary на подпроцесс)
- Уменьшение сложности основной диаграммы
Пример — подпроцесс «Обработать платёж»:
┌─────────────────────────────────────┐
│ ⬔ Обработать платёж [+] ╗
│ (содержимое видно при развороте): │
│ ○ → ⬭ Проверить карту → ◇ XOR │
│ ├── [OK] → ⬭ Списать → ◉ │
│ └── [Нет] → ⬭ Отклонить → ◉ │
└─────────────────────────────────────┘
│
◯❌ (Error Boundary на подпроцесс — ловит ошибки из любого шага внутри)
5.2. Reusable Subprocess (Call Activity) — переиспользуемый
Визуал: Скруглённый прямоугольник с толстой границей и знаком +. Отличие от Embedded — ссылается на отдельный .bpmn-файл.
Когда использовать:
- Один и тот же подпроцесс используется в нескольких процессах
- Подпроцесс поддерживается отдельной командой
- Подпроцесс может быть заменён без изменения родительского процесса
5.3. Event Subprocess — подпроцесс, запускаемый событием
Особенность: Не вызывается по Sequence Flow, а активируется событием (ошибка, сигнал, таймер) во время выполнения родительского процесса.
Пример: Параллельный обработчик ошибок.
Родительский процесс:
⬭ Task A → ⬭ Task B → ⬭ Task C
Event Subprocess (прикреплён к процессу):
◯❌ (Error Start) → ⬭ Записать ошибку → ⬭ Уведомить админа
Если в любой задаче возникает ошибка — Event Subprocess запускается параллельно.
6. Пример: Комплексная BPMN-диаграмма «Обработка заказа интернет-магазина»
6.1. Легенда
Интернет-магазин. Заказ поступил. Нужно проверить наличие, подтвердить, оплатить, отгрузить.
6.2. Пулы и участники
| Пул | Тип | Роль |
|---|---|---|
| Клиент | Свёрнутый (Collapsed) | Внешний заказчик |
| Интернет-магазин | Развёрнутый | Система + менеджер + склад |
| Платёжный шлюз | Свёрнутый (Collapsed) | Внешняя система Stripe |
6.3. Диаграмма (текстовое описание с элементами)
Pool: Интернет-магазин
┌─────────────────────────────────────────────────────────────────────────────┐
│ Lane: Система │
│ │
│ ○✉ (Start: Заказ получен от клиента) │
│ │ │
│ ▼ │
│ ⬭ Service: "Проверить наличие товара на складе" │
│ │ │
│ ◇ XOR [Товар в наличии?] │
│ ├── [Да] ───────────────────────────────────────────────────┐ │
│ │ ⬭ Service: "Рассчитать стоимость с доставкой" │ │
│ │ ⬭ Send: "Отправить подтверждение клиенту" │ │
│ │ ⬭ User: "Подтвердить заказ" (менеджер) │ │
│ │ │ │ │ │
│ │ │ ◯⏰ Boundary Timer (Interrupting, 1 час) │ │
│ │ │ │ └── ⬭ Service: "Отменить заказ" │ │
│ │ │ │ │
│ │ ◯✉ (Intermediate Catch: "Ожидать оплату") │ │
│ │ │ │ │ │
│ │ │ ◯⏰ Boundary Timer (Non-Interrupting, 12ч) │ │
│ │ │ │ └── ⬭ Send: "Напомнить об оплате" │ │
│ │ │ │ │
│ │ ✦ Event-based Gateway │ │
│ │ ├── ◯✉ (Message: "Платёж подтверждён") │ │
│ │ │ │ │ │
│ │ │ ▼ │ │
│ │ │ ⬭ Service: "Передать заказ на склад" │ │
│ │ │ │ │
│ │ └── ◯⏰ (Timer: "24 часа прошло") │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ⬭ Service: "Отменить заказ (не оплачен)" │ │
│ │ │ │
│ └── [Нет] ───────────────────────────────────────────────────┘ │
│ ⬭ Service: "Отклонить заказ" │
│ │
│ ◉✉ (End: Уведомить клиента о статусе) │
└─────────────────────────────────────────────────────────────────────────────┘
│ Lane: Менеджер │
│ ⬭ User: "Подтвердить заказ" — видит в Tasklist │
│ ◯⏰ Boundary Timer (1 час, Interrupting) │
│ │
│ Lane: Склад │
│ ⬭ Service: "Подготовить товар к отгрузке" → ◉✉ │
└─────────────────────────────────────────────────────────────────────────────┘
6.4. Какие элементы BPMN используются
| Элемент | Тип | Обоснование |
|---|---|---|
| ○✉ | Start Message | Процесс начинается с получения заказа от клиента |
| ⬭⚙ | Service Task | Автоматическая проверка наличия (API к складской системе) |
| ◇✕ | Exclusive Gateway (XOR) | Выбор по данным: «Товар есть?» |
| ⬭🧑 | User Task | Менеджер подтверждает заказ |
| ◯⏰ | Boundary Timer (Interrupting) | 1 час на подтверждение, иначе — отмена |
| ◯⏰ | Boundary Timer (Non-Interrupting) | 12 часов — напомнить об оплате |
| ✦ | Event-based Gateway | Ждём: платёж или тайм-аут 24ч |
| ◯✉ | Intermediate Catch Message | Ожидание подтверждения платежа |
| ◯⏰ | Intermediate Catch Timer | Тайм-аут оплаты |
| ◉✉ | End Message | Уведомить клиента |
7. Сводная таблица: какой элемент когда использовать
| Ситуация | Элемент |
|---|---|
| Процесс запускается HTTP-запросом | Start Event (Message) |
| Процесс запускается по расписанию | Start Event (Timer) |
| Человек заполняет форму в системе | User Task |
| Система вызывает внешнее API | Service Task |
| Нужно выполнить бизнес-правила (таблица) | Business Rule Task (DMN) |
| Нужно отправить уведомление другому пулу | Send Task |
| Нужно ждать сообщение от другого пула | Intermediate Catch Message Event |
| Нужно выбрать один из двух путей по условию | Exclusive Gateway (XOR) |
| Нужно выполнить несколько путей одновременно | Parallel Gateway (AND) |
| Нужно выбрать все истинные пути | Inclusive Gateway (OR) |
| Нужно выбрать по первому наступившему событию | Event-based Gateway |
| Нужно прервать задачу по тайм-ауту | Boundary Timer (Interrupting) |
| Нужно напомнить, не прерывая задачу | Boundary Timer (Non-Interrupting) |
| Нужно обработать ошибку в Service Task | Error Boundary Event |
| Нужно «убить» все параллельные потоки | End Event (Terminate) |
| Нужно сгруппировать шаги | Embedded Subprocess |
| Нужно переиспользовать процесс в разных местах | Call Activity |
| Нужна ручная операция вне системы | Manual Task |
| Нужен небольшой скрипт | Script Task |
8. Чек-лист: правильность выбора элементов
| № | Вопрос | Проверка |
|---|---|---|
| 1 | Start Event соответствует триггеру процесса? (Message / Timer / None) | Да / Нет |
| 2 | Service Task имеет полное ТЗ с JSON-контрактами? | Да / Нет |
| 3 | User Task имеет assignee и SLA? | Да / Нет |
| 4 | За Event-based Gateway следуют только Intermediate Catch Events? | Да / Нет |
| 5 | У Event-based Gateway есть Timer (страховка от вечного ожидания)? | Да / Нет |
| 6 | Boundary Timer (Interrupting) стоит, если нужно прервать задачу? | Да / Нет |
| 7 | Boundary Timer (Non-Interrupting) стоит для напоминаний? | Да / Нет |
| 8 | У XOR есть default flow? (иначе — incident при исполнении) | Да / Нет |
| 9 | У Inclusive Gateway есть Inclusive Merge? | Да / Нет |
| 10 | Terminate End используется только когда нужно убить все потоки? | Да / Нет |
| 11 | Вложенные подпроцессы не превышают 3 уровней? | Да / Нет |
9. Вопросы для самопроверки
- Какие 3 положения событий существуют в BPMN? Какие из них Catching, а какие Throwing?
- Почему после Event-based Gateway нельзя поставить Exclusive Gateway? (объясните причину)
- Что такое Boundary Event? В чём разница между Interrupting и Non-Interrupting?
- Приведите пример, когда нужно использовать Non-Interrupting Boundary Timer, а не Interrupting.
- Какие поля должны быть в техническом задании на Service Task? Перечислите минимум 5.
- Чем Send Task отличается от Service Task?
- Что произойдёт, если у Event-based Gateway нет ни одной Timer-ветки?
- Когда использовать End Event (Terminate), а когда обычный End (None)?
- Чем Embedded Subprocess отличается от Call Activity?
- Какие типы Boundary Events бывают только Interrupting?
- Что такое BPMN Error и как он связывается с Error Boundary Event?
- В каком случае Inclusive Gateway (OR) лучше, чем несколько Parallel Gateway (AND)?
10. Практическое задание
Задание 1. Определите тип элемента
Для каждого фрагмента определите: категорию, тип, маркер, является ли Catching или Throwing.
| Фрагмент | Категория | Тип | Маркер | C/Th |
|---|---|---|---|---|
| ○✉ в начале процесса | ||||
| ⬭ с иконкой 🧑 | N/A | |||
| ⬭ с иконкой ⚙ | N/A | |||
| ◇ с ✕ внутри | N/A | |||
| ◯⏰ на границе задачи (сплошная линия) | C | |||
| ◯⏰ на границе задачи (пунктирная линия) | C | |||
| ✦ (пятиугольник) | N/A | |||
| ◉ с ✉ внутри | ||||
| ◯✉ после Event-based Gateway | ||||
| ◉● (жирный круг) |
Задание 2. Исправьте Event-based Gateway
Дана неправильная BPMN-диаграмма. Найдите ошибку и исправьте.
Исходный фрагмент (с ошибкой):
⬭ Отправить запрос на согласование
│
✦ (Event-based Gateway)
├── ◇ XOR [Руководитель одобрил?] ← Какая здесь ошибка?
│ ├── [Да] → ⬭ Продолжить
│ └── [Нет] → ⬭ Отклонить
└── ◯⏰ (Timer: 3 дня прошло)
│
▼
⬭ Эскалировать
Вопросы:
- Почему это неправильно?
- Перепишите этот фрагмент корректно — с правильными элементами после Event-based Gateway.
Задание 3. Спроектируйте процесс с Service Task (JSON-контракт)
Кейс: Процесс регистрации нового пользователя. После проверки документов менеджером нужно вызвать внешний API для отправки SMS с одноразовым кодом.
Задание:
- Нарисуйте фрагмент BPMN-диаграммы: User Task «Проверить документы» → Service Task «Отправить SMS-код» → Exclusive Gateway (XOR) «SMS отправлена?»
- Составьте ТЗ на Service Task:
- Какой HTTP-метод?
- Какой URL?
- Входной JSON (поля и типы)
- Выходной JSON (поля и типы)
- Тайм-аут
- Retry policy
- Что делать при ошибке (500, 4xx, тайм-аут)
Задание 4. Boundary Events — SLA для процесса закупки
Кейс: Процесс согласования заявки на закупку.
Правила:
- Сотрудник отправляет заявку
- Руководитель отдела должен согласовать в течение 24 часов
- Через 12 часов — напоминание руководителю (email)
- Через 24 часа — эскалация финансовому директору (задача прерывается у руководителя)
- Финансовый директор должен согласовать в течение 48 часов с момента эскалации
- Через 24 часа после эскалации — напоминание финансовому директору
- Через 48 часов после эскалации — автоматический отказ заявки
Задание:
- Нарисуйте BPMN-фрагмент в текстовом виде. Используйте правильные Boundary Events (Interrupting / Non-Interrupting).
- Объясните, почему вы выбрали Interrupting или Non-Interrupting для каждого таймера.
- Как изменится схема, если руководитель может согласовать заявку и после 24 часов (например, с комментарием «опоздал, но согласую»)?
Задание 5. Анализ фрагмента
Дан фрагмент BPMN-диаграммы. Определите все ошибки (минимум 3):
⬭ Service: "Проверить кредитную историю"
│
✦ (Event-based Gateway)
├── [Кредитная история хорошая] → ⬭ Одобрить заявку
├── [Кредитная история плохая] → ⬭ Отклонить заявку
└── ⬭ (следующий шаг без события)
Вопросы:
- В чём ошибка этого фрагмента?
- Каким должен быть правильный BPMN-фрагмент, если решение по кредитной истории принимается на основе данных?
- А если решение принимается на основе событий (например, внешний скоринговый сервис асинхронно возвращает результат)?
11. Дополнительные материалы
- Спецификация: BPMN 2.0 — раздел «Flow Objects» (главы 10–13, 50+ страниц формального описания)
- Практика: Camunda Modeler — откройте палитру «Events», «Tasks», «Gateways» и изучите все варианты
- Книга: Bruce Silver — «BPMN Method and Style» — лучший учебник по корректному стилю моделирования
- Книга: Thomas Allweyer — «BPMN 2.0: Introduction to the Standard for Business Process Modeling»
- Видео: Camunda BPMN Tutorial Series — YouTube, плейлист из 20+ видео по элементам BPMN
- Шпаргалка: «BPMN Events Cheat Sheet» — полная матрица событий (~50 комбинаций) — скачайте с bpmnquickguide.com
- Инструмент: Camunda Modeler — camunda.com/download/modeler/ — создавайте и проверяйте диаграммы
- Статья: «BPMN 2.0 – Gateways» на camunda.com — официальное описание шлюзов
- Шаблоны: Camunda Modeler → File → New from Template → «Empty» или «SLA Process» (если есть в вашей версии)
Следующий урок: 05.03 — Продвинутые паттерны BPMN: Saga, Compensation, Escalation