Представьте согласование кредита в банке. Клиент подал заявку — и дальше начинается процесс, который тянется днями. Скоринговый сервис проверяет платёжеспособность, служба безопасности смотрит документы, где-то в середине заявка ждёт решения человека, который может уйти на выходные. Если внешний сервис не ответил — надо подождать и попробовать снова через час. Если на последнем шаге пришёл отказ — надо откатить всё, что уже сделали: снять резерв средств, отменить предодобрение. Такой процесс живёт часами и днями, проходит через людей, сервисы, таймеры и откаты. Обычная стейт-машина в памяти здесь не спасёт: перезапустишь приложение — и все «висящие» заявки потеряют своё состояние. Хранить состояние в базе руками можно, но быстро упираешься в кучу однообразного кода: где мы сейчас, чего ждём, что уже сделано, что откатывать. Для таких длинных процессов есть отдельный класс инструментов — BPM-движки. Разберём, что это и когда оно нужно.

Что такое BPM-движок

BPM-движок (Business Process Management) — это программа, которая управляет длинными бизнес-процессами за вас. Там, где стейт-машина в памяти хранит текущее состояние в переменной, BPM-движок берёт на себя всё, что нужно долгоживущему процессу:

  • Персистентное состояние. Движок записывает в базу, на каком шаге сейчас каждый экземпляр процесса. Перезапустили приложение — процессы продолжаются с того же места, ничего не потеряно.
  • Ожидание событий и таймеров. Процесс может «уснуть» на середине и ждать: внешнего сообщения, ответа человека, наступления даты. «Через 3 дня без ответа — напомнить», «дождаться подтверждения оплаты» — это встроенные возможности, а не код, который вы пишете сами.
  • Повторные попытки (ретраи). Если шаг обратился к внешнему сервису и тот не ответил, движок сам повторит попытку по заданной политике, а не уронит весь процесс.
  • История выполнения. По каждому экземпляру видно, какие шаги прошли, когда, сколько заняли, где застряли. Это готовая картина процесса для аналитики и разбора инцидентов.

Сам процесс обычно описывают на языке-схеме BPMN (Business Process Model and Notation). Это графическая нотация: прямоугольники — задачи, ромбы — развилки (условия), кружки — старт и финиш, значки конвертов — ожидание сообщений. Ключевая идея в том, что такая схема исполняемая: движок не просто рисует картинку, а буквально идёт по ней, шаг за шагом. Аналитик и разработчик смотрят на одну и ту же диаграмму, и это же — работающая логика.

старт Скоринг решение? да Резерв ждём человека Выдача готово нет отказ

Тот же кредитный процесс как исполняемая BPMN-схема: кружки — старт и финиш, прямоугольники — задачи, ромб — развилка, конверт — ожидание решения человека. BPM-движок буквально идёт по этой схеме, помнит, где сейчас каждая заявка, и переживает перезапуски приложения.

Camunda

Camunda — один из самых известных BPM-движков в мире Java. Его суть в том, что нарисованная BPMN-схема становится исполняемой напрямую: вы моделируете процесс в визуальном редакторе, привязываете к задачам свой код, и движок гоняет по этой схеме реальные заявки.

Где применяют Camunda:

  • Согласования — заявки на кредит, отпуск, закупку, доступ; всё, что проходит через несколько проверок и решений людей.
  • Платежи — многошаговые денежные операции, где важны ретраи, ожидания и откаты.
  • Приём клиента или сотрудника — регистрация: собрать документы, проверить, завести учётные записи, уведомить.

Стоит знать про две большие версии, они устроены по-разному:

  • Camunda 7 — классический встраиваемый движок. Он живёт внутри вашего Java-приложения как библиотека и хранит состояние процессов в вашей же реляционной базе. Простой старт: подключил зависимость — и в том же сервисе крутятся процессы.
  • Camunda 8 — переработанная версия поверх ядра Zeebe. Это уже не встраиваемая библиотека, а отдельный распределённый движок, рассчитанный на большой поток процессов и работу с множеством микросервисов. Ваш код общается с ним по сети, а не внутри одной JVM.

Если коротко: 7 — «движок внутри моего приложения», 8 — «движок как отдельная масштабируемая система рядом».

Аналоги

Camunda — не единственный вариант. Инструментов много, и различаются они прежде всего тем, как описывают процесс и как разворачиваются.

  • Temporal — durable execution: процесс пишется как обычный код (на Java, Go и других языках), а не как BPMN-схема; движок гарантирует, что этот код переживёт перезапуски и падения и продолжится с того же места.
  • Zeebe — облачно-нативное ядро Camunda 8: отдельный распределённый движок, заточенный под оркестрацию множества микросервисов с высокой пропускной способностью.
  • Flowable и Activiti — лёгкие встраиваемые BPMN-движки из той же семьи, что и Camunda 7 (общие корни), удобны, когда нужен встроенный движок без тяжёлой инфраструктуры.
  • jBPM — BPMN-движок от Red Hat, часто идёт в связке с правилами (Drools), когда в процессе много бизнес-логики «если — то».
  • AWS Step Functions — управляемый (managed) оркестратор в облаке AWS: процесс описывают декларативно, а сервер держит и обслуживает сам провайдер, вам не нужно ничего разворачивать.

По одному предложению на каждый — этого достаточно, чтобы отличать их на слух. Дальше выбор зависит от того, хотите вы процесс как схему или как код, встроенный движок или отдельный, свой развёрнутый или облачный.

BPM и saga

Отдельно стоит связать BPM-движок с распределёнными системами. Когда бизнес-процесс идёт через несколько микросервисов, возникает проблема: единой транзакции на всех больше нет. Сервис заказов, сервис платежей, сервис склада — у каждого своя база, и «откатить всё разом» одним rollback невозможно.

Здесь и появляется паттерн saga — цепочка локальных шагов, где у каждого шага есть компенсирующее действие на случай неудачи. Списали деньги, но товара не оказалось — выполняем компенсацию: возвращаем деньги. Saga — это по сути та же долгая последовательность шагов с откатами, что мы описывали в начале.

BPM-движок отлично играет роль оркестратора saga: он держит центральную схему процесса, вызывает сервисы по очереди, ждёт их ответов и при неудаче на позднем шаге запускает компенсации в обратном порядке. Состояние saga (где мы, что уже сделали, что компенсировать) движок хранит персистентно — как раз то, чего не хватало стейт-машине в памяти. Так BPM-движок становится инструментом распределённой согласованности: не мгновенной, а достигаемой через последовательность шагов и компенсаций.

// Схематично: оркестратор saga вызывает шаги по очереди
// и при провале запускает компенсации в обратном порядке.
public void executeOrderSaga(Order order) {
    reservePayment(order);       // шаг 1
    try {
        reserveStock(order);     // шаг 2 — тут может не хватить товара
    } catch (OutOfStockException e) {
        refundPayment(order);    // компенсация шага 1
        throw e;
    }
}

В реальном BPM-движке эту логику не пишут таким кодом — её рисуют схемой, а движок сам помнит состояние и переживает перезапуски. Пример выше нужен лишь чтобы показать идею «шаг → компенсация».

Когда своя стейт-машина, а когда BPM-движок

Главный практический вопрос: тащить ли в проект целый движок или хватит своей маленькой стейт-машины. Ответ зависит от природы процесса.

Своя стейт-машина (в коде, с состоянием в базе через enum-поле или Spring Statemachine) подходит, когда:

  • процесс живёт внутри одного сервиса, не размазан по системам;
  • переходы быстрые — секунды, а не дни;
  • состояние легко уложить в одну колонку status в вашей же таблице;
  • участие людей и длинных таймеров минимально;
  • не нужна отдельная система для просмотра истории процессов — хватает обычных логов.

Классический пример — статус заказа: NEW → PAID → SHIPPED → DELIVERED. Пара переходов, всё в одном сервисе, состояние в поле записи. Тянуть сюда Camunda — стрелять из пушки по воробьям.

BPM-движок оправдан, когда:

  • процесс долгий и межсервисный — идёт часами и днями через несколько сервисов;
  • нужны таймеры и ожидания — «напомнить через 3 дня», «дождаться оплаты»;
  • в процессе участвуют люди — согласования, ручные проверки, задачи в списке дел;
  • важна видимость и история — бизнесу нужно видеть, где застряла каждая заявка и сколько времени заняли шаги;
  • нужны встроенные ретраи и компенсации через оркестрацию saga.

Правило простое: короткий процесс в одном сервисе — своя стейт-машина; долгий процесс через людей, таймеры и несколько сервисов, где нужна видимость, — BPM-движок. Промежуточный случай (один сервис, но много состояний и переходов) закрывает библиотека вроде Spring Statemachine — это середина между самописным enum и полноценным движком.

Коротко

  • Долгие процессы (согласования, платежи, приём сотрудников) живут днями, ждут людей и таймеров, требуют ретраев и откатов — стейт-машина в памяти не переживёт перезапуск, а состояние в базе руками означает много однообразного кода.
  • BPM-движок берёт это на себя: персистентное состояние, ожидание событий и таймеров, ретраи, история выполнения; процесс описывают исполняемой BPMN-схемой.
  • Camunda превращает BPMN-схему в работающую логику; версия 7 — встраиваемая библиотека, версия 8 (на ядре Zeebe) — отдельный масштабируемый движок.
  • Аналоги различаются подходом: Temporal — процесс как код, Zeebe — облачно-нативная оркестрация микросервисов, Flowable/Activiti и jBPM — встраиваемые BPMN-движки, AWS Step Functions — управляемый облачный оркестратор.
  • В распределённой системе BPM-движок работает оркестратором saga: ведёт шаги по сервисам и запускает компенсации при неудаче — это способ достичь согласованности без общей транзакции.
  • Выбор: короткий процесс в одном сервисе — своя стейт-машина; долгий межсервисный процесс с людьми, таймерами и потребностью в видимости — BPM-движок.