Camunda v7

Основные концепции

Process Engine (Движок процессов)

Назначение: Ядро Camunda, которое выполняет BPMN процессы, управляет состоянием процессов и координирует выполнение задач.

Ключевые компоненты:

  • Process Definition - описание процесса в BPMN формате
  • Process Instance - конкретный экземпляр выполняемого процесса
  • Activity Instance - состояние выполнения конкретной активности
  • Execution - указатель на текущую позицию в процессе
// Получение Process Engine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
TaskService taskService = processEngine.getTaskService();

// Запуск процесса
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
    "orderProcess", 
    Map.of("orderId", "12345", "amount", 1000)
);

BPMN элементы

Start Event - точка начала процесса, может быть триггером по времени, сообщению или событию.

Service Task - автоматически выполняемая задача, реализуется через Java класс или внешний сервис.

User Task - задача, требующая человеческого участия, появляется в task list.

Gateway - узел принятия решений:

  • Exclusive Gateway - выбирает один путь из нескольких
  • Parallel Gateway - разделяет поток на параллельные ветки
  • Inclusive Gateway - может активировать несколько путей
// Service Task Implementation
@Component
public class OrderValidator implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        String orderId = (String) execution.getVariable("orderId");
        // Валидация заказа
        boolean isValid = validateOrder(orderId);
        execution.setVariable("orderValid", isValid);
    }
}

Deployment и Process Definition

Deployment (Развертывание)

Процесс: Загрузка BPMN файлов в Process Engine, создание Process Definition из BPMN диаграммы.

Способы развертывания:

  • Автоматическое через processes.xml и @EnableProcessApplication
  • Программное через RepositoryService
  • Через REST API или Camunda Admin
// Программное развертывание
@PostConstruct
public void deployProcess() {
    repositoryService.createDeployment()
        .addClasspathResource("processes/order-process.bpmn")
        .name("Order Process Deployment")
        .deploy();
}

// Получение Process Definition
ProcessDefinition definition = repositoryService
    .createProcessDefinitionQuery()
    .processDefinitionKey("orderProcess")
    .latestVersion()
    .singleResult();

Versioning (Версионирование)

Принцип: Каждый deployment создает новую версию Process Definition. Активные процессы продолжают выполняться на старой версии.

Управление версиями:

  • Новые процессы запускаются на последней версии
  • Можно принудительно запустить на конкретной версии
  • Миграция процессов между версиями возможна
// Запуск на конкретной версии
runtimeService.startProcessInstanceById(
    "orderProcess:2:12345" // processDefinitionId с версией
);

// Миграция активных процессов
MigrationPlan migrationPlan = runtimeService
    .createMigrationPlan("orderProcess:1:123", "orderProcess:2:456")
    .mapActivities("oldTaskId", "newTaskId")
    .build();

runtimeService.newMigration(migrationPlan)
    .processInstanceIds(processInstanceIds)
    .execute();

Variables и Scope

Process Variables (Переменные процесса)

Назначение: Хранение данных процесса, доступных на всех уровнях выполнения.

Типы данных:

  • Примитивы (String, Integer, Boolean, Date)
  • Serializable объекты
  • JSON объекты
  • File variables для больших данных
// Установка переменных при старте
Map<String, Object> variables = Map.of(
    "customerId", "CUST001",
    "orderAmount", 1500.0,
    "orderDate", new Date(),
    "orderItems", Arrays.asList("item1", "item2")
);

ProcessInstance instance = runtimeService.startProcessInstanceByKey(
    "orderProcess", variables
);

// Получение переменных
Integer amount = (Integer) runtimeService.getVariable(
    instance.getId(), "orderAmount"
);

Variable Scope (Область видимости)

Уровни scope:

  • Process Instance - глобальные переменные процесса
  • Execution - переменные конкретного execution (в параллельных ветках)
  • Task - локальные переменные задачи
// Переменные на уровне execution
execution.setVariableLocal("branchData", "branch1");

// Переменные на уровне task
taskService.setVariableLocal(taskId, "userInput", "approved");

// Получение переменных с учетом scope
Map<String, Object> variables = runtimeService.getVariables(executionId);
Map<String, Object> localVars = runtimeService.getVariablesLocal(executionId);

Task Management

User Tasks (Пользовательские задачи)

Назначение: Задачи, требующие участия человека, управляются через Task Service.

Свойства задачи:

  • Assignee - назначенный пользователь
  • Candidate Users/Groups - потенциальные исполнители
  • Due Date - срок выполнения
  • Priority - приоритет
// Получение задач пользователя
List<Task> tasks = taskService.createTaskQuery()
    .taskAssignee("john.doe")
    .orderByDueDate()
    .asc()
    .list();

// Задачи группы
List<Task> groupTasks = taskService.createTaskQuery()
    .taskCandidateGroup("sales")
    .list();

// Назначение задачи
taskService.setAssignee(taskId, "john.doe");

// Завершение задачи
taskService.complete(taskId, Map.of("approved", true));

Task Forms (Формы задач)

Назначение: Определение пользовательского интерфейса для выполнения задач.

Типы форм:

  • Generated Forms - автоматически генерируемые из form fields
  • External Forms - кастомные HTML формы
  • Embedded Forms - встроенные в Camunda Tasklist
// Form Fields в BPMN
<userTask id="approveOrder" name="Approve Order">
  <extensionElements>
    <camunda:formData>
      <camunda:formField id="amount" label="Amount" type="long" />
      <camunda:formField id="approved" label="Approved" type="boolean" />
    </camunda:formData>
  </extensionElements>
</userTask>

// Получение form data
TaskFormData formData = formService.getTaskFormData(taskId);
List<FormField> fields = formData.getFormFields();

External Tasks

External Task Pattern

Назначение: Выполнение задач внешними приложениями через polling механизм. Позволяет интегрировать микросервисы с Camunda.

Преимущества:

  • Слабая связанность с Process Engine
  • Масштабируемость worker'ов
  • Fault tolerance через retry механизмы
// Определение External Task в BPMN
<serviceTask id="processPayment" name="Process Payment">
  <extensionElements>
    <camunda:properties>
      <camunda:property name="type" value="payment" />
    </camunda:properties>
  </extensionElements>
</serviceTask>

// External Task Worker
@Component
public class PaymentWorker {
    
    @EventListener
    public void startWorker(ApplicationReadyEvent event) {
        ExternalTaskClient client = ExternalTaskClient.create()
            .baseUrl("http://localhost:8080/engine-rest")
            .build();
            
        client.subscribe("payment")
            .lockDuration(10000)
            .handler(this::handlePayment)
            .open();
    }
    
    private void handlePayment(ExternalTask task, ExternalTaskService service) {
        try {
            String orderId = task.getVariable("orderId");
            // Обработка платежа
            processPayment(orderId);
            
            service.complete(task, Map.of("paymentStatus", "SUCCESS"));
        } catch (Exception e) {
            service.handleFailure(task, e.getMessage(), "Payment failed", 3, 5000);
        }
    }
}

Retry и Error Handling

Механизмы обработки ошибок:

  • Retry - повторная попытка выполнения
  • Incident - создание инцидента при исчерпании попыток
  • Error Boundary Events - перехват ошибок в BPMN
// Настройка retry в External Task
service.handleFailure(task, 
    "Payment service unavailable",  // errorMessage
    "Service temporarily down",      // errorDetails
    3,                              // retries
    5000                            // retryTimeout
);

// BPMN Error Event
throw new BpmnError("PAYMENT_FAILED", "Payment could not be processed");

Process Monitoring

History Service

Назначение: Предоставляет информацию о завершенных процессах, задачах и активностях для анализа и аудита.

Уровни истории:

  • NONE - история не сохраняется
  • ACTIVITY - сохраняются активности
  • VARIABLE - сохраняются изменения переменных
  • FULL - полная история включая form properties
// Поиск завершенных процессов
List<HistoricProcessInstance> completedProcesses = historyService
    .createHistoricProcessInstanceQuery()
    .processDefinitionKey("orderProcess")
    .finished()
    .finishedAfter(Date.from(Instant.now().minus(30, ChronoUnit.DAYS)))
    .orderByProcessInstanceEndTime()
    .desc()
    .list();

// Анализ времени выполнения
long avgDuration = historyService
    .createHistoricProcessInstanceQuery()
    .processDefinitionKey("orderProcess")
    .finished()
    .list()
    .stream()
    .mapToLong(p -> p.getDurationInMillis())
    .sum() / completedProcesses.size();

Metrics и KPIs

Метрики процесса:

  • Cycle Time - время от начала до завершения
  • Throughput - количество завершенных процессов
  • Bottlenecks - узкие места в процессе
// Custom metrics
@Component
public class ProcessMetrics {
    
    @EventListener
    public void onProcessEnd(ProcessEndEvent event) {
        long duration = event.getProcessInstance().getDurationInMillis();
        meterRegistry.timer("process.duration", "processKey", 
            event.getProcessInstance().getProcessDefinitionKey())
            .record(duration, TimeUnit.MILLISECONDS);
    }
    
    @EventListener
    public void onTaskComplete(TaskCompleteEvent event) {
        meterRegistry.counter("tasks.completed", "taskKey", 
            event.getTask().getTaskDefinitionKey())
            .increment();
    }
}

Integration Patterns

Message Events

Назначение: Асинхронная коммуникация между процессами или внешними системами через события.

Типы:

  • Message Start Event - запуск процесса по сообщению
  • Message Intermediate Event - ожидание сообщения в процессе
  • Message End Event - отправка сообщения при завершении
// Отправка сообщения в процесс
runtimeService.createMessageCorrelation("OrderApproved")
    .setVariable("approvedBy", "manager")
    .processInstanceId(processInstanceId)
    .correlate();

// Message Listener
@Component
public class OrderMessageListener {
    
    @JmsListener(destination = "order.approved")
    public void handleOrderApproved(OrderApprovedMessage message) {
        runtimeService.createMessageCorrelation("OrderApproved")
            .setVariable("orderId", message.getOrderId())
            .correlate();
    }
}

Signal Events

Назначение: Broadcast события для уведомления множества процессов одновременно.

// Отправка сигнала всем процессам
runtimeService.signalEventReceived("MarketClosed");

// Отправка сигнала конкретному процессу
runtimeService.signalEventReceived("OrderCancelled", processInstanceId);

REST API Integration

Назначение: Интеграция с внешними REST сервисами через HTTP Connector.

// HTTP Connector в Service Task
<serviceTask id="callExternalService" name="Call External Service">
  <extensionElements>
    <camunda:connector>
      <camunda:connectorId>http-connector</camunda:connectorId>
      <camunda:inputOutput>
        <camunda:inputParameter name="url">http://api.example.com/orders</camunda:inputParameter>
        <camunda:inputParameter name="method">POST</camunda:inputParameter>
        <camunda:inputParameter name="headers">
          <camunda:map>
            <camunda:entry key="Content-Type">application/json</camunda:entry>
          </camunda:map>
        </camunda:inputParameter>
        <camunda:outputParameter name="response">${response}</camunda:outputParameter>
      </camunda:inputOutput>
    </camunda:connector>
  </extensionElements>
</serviceTask>

Performance и Optimization

Database Optimization

Важные аспекты:

  • Indexes - на часто запрашиваемые поля
  • History Cleanup - регулярная очистка истории
  • Batch Operations - для массовых операций
// Batch operations
Batch batch = runtimeService.deleteProcessInstancesAsync(
    Arrays.asList("proc1", "proc2", "proc3"),
    null, // deleteReason
    true, // skipCustomListeners
    true  // skipSubprocesses
);

// History cleanup
@Scheduled(cron = "0 0 2 * * ?") // Каждый день в 2:00
public void cleanupHistory() {
    historyService.cleanUpHistoryAsync(
        HistoryCleanupConfiguration.builder()
            .batchSize(1000)
            .degreeOfParallelism(2)
            .build()
    );
}

Memory Management

Оптимизации:

  • Ограничение размера переменных
  • Использование External Tasks для тяжелых операций
  • Proper connection pooling
// Оптимизация запросов
List<Task> tasks = taskService.createTaskQuery()
    .taskAssignee("user")
    .active()
    .orderByDueDate()
    .asc()
    .listPage(0, 50); // Пагинация

// Lazy loading переменных
Map<String, Object> variables = runtimeService.getVariables(
    executionId, Arrays.asList("orderId", "amount") // Только нужные переменные
);