Элементы процесса (события, задачи, шлюзы)

Урок 2 из 3

Урок 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: Остальные промежуточные события деактивируются (отменяются)

Аналогия: Вы ожидаете в аэропорту рейс. У вас три варианта:

  1. ✉ — объявили посадку (Message)
  2. ⏰ — через 30 минут закончилась регистрация (Timer)
  3. ✉ — пришло 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 часов на согласование. Нужно:

  1. Через 24 часа — напомнить (не прерывая задачу)
  2. Через 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: "Уведомить администратора"

Как работает:

  1. Service Task выбрасывает ошибку (BPMN Error)
  2. Error Boundary «ловит» её на границе задачи
  3. Service Task прерывается
  4. Процесс идёт по пути обработки ошибки

Важно: 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. Вопросы для самопроверки

  1. Какие 3 положения событий существуют в BPMN? Какие из них Catching, а какие Throwing?
  2. Почему после Event-based Gateway нельзя поставить Exclusive Gateway? (объясните причину)
  3. Что такое Boundary Event? В чём разница между Interrupting и Non-Interrupting?
  4. Приведите пример, когда нужно использовать Non-Interrupting Boundary Timer, а не Interrupting.
  5. Какие поля должны быть в техническом задании на Service Task? Перечислите минимум 5.
  6. Чем Send Task отличается от Service Task?
  7. Что произойдёт, если у Event-based Gateway нет ни одной Timer-ветки?
  8. Когда использовать End Event (Terminate), а когда обычный End (None)?
  9. Чем Embedded Subprocess отличается от Call Activity?
  10. Какие типы Boundary Events бывают только Interrupting?
  11. Что такое BPMN Error и как он связывается с Error Boundary Event?
  12. В каком случае 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 дня прошло)
        │
        ▼
    ⬭ Эскалировать

Вопросы:

  1. Почему это неправильно?
  2. Перепишите этот фрагмент корректно — с правильными элементами после Event-based Gateway.

Задание 3. Спроектируйте процесс с Service Task (JSON-контракт)

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

Задание:

  1. Нарисуйте фрагмент BPMN-диаграммы: User Task «Проверить документы» → Service Task «Отправить SMS-код» → Exclusive Gateway (XOR) «SMS отправлена?»
  2. Составьте ТЗ на Service Task:
    • Какой HTTP-метод?
    • Какой URL?
    • Входной JSON (поля и типы)
    • Выходной JSON (поля и типы)
    • Тайм-аут
    • Retry policy
    • Что делать при ошибке (500, 4xx, тайм-аут)

Задание 4. Boundary Events — SLA для процесса закупки

Кейс: Процесс согласования заявки на закупку.

Правила:

  1. Сотрудник отправляет заявку
  2. Руководитель отдела должен согласовать в течение 24 часов
  3. Через 12 часов — напоминание руководителю (email)
  4. Через 24 часа — эскалация финансовому директору (задача прерывается у руководителя)
  5. Финансовый директор должен согласовать в течение 48 часов с момента эскалации
  6. Через 24 часа после эскалации — напоминание финансовому директору
  7. Через 48 часов после эскалации — автоматический отказ заявки

Задание:

  1. Нарисуйте BPMN-фрагмент в текстовом виде. Используйте правильные Boundary Events (Interrupting / Non-Interrupting).
  2. Объясните, почему вы выбрали Interrupting или Non-Interrupting для каждого таймера.
  3. Как изменится схема, если руководитель может согласовать заявку и после 24 часов (например, с комментарием «опоздал, но согласую»)?

Задание 5. Анализ фрагмента

Дан фрагмент BPMN-диаграммы. Определите все ошибки (минимум 3):

⬭ Service: "Проверить кредитную историю"
    │
    ✦ (Event-based Gateway)
    ├── [Кредитная история хорошая] → ⬭ Одобрить заявку
    ├── [Кредитная история плохая] → ⬭ Отклонить заявку
    └── ⬭ (следующий шаг без события)

Вопросы:

  1. В чём ошибка этого фрагмента?
  2. Каким должен быть правильный BPMN-фрагмент, если решение по кредитной истории принимается на основе данных?
  3. А если решение принимается на основе событий (например, внешний скоринговый сервис асинхронно возвращает результат)?

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

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

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

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

🎬 BPMN 2

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

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