«1. Обзор

Flowable — это механизм бизнес-процессов, написанный на Java. В этом руководстве мы подробно рассмотрим бизнес-процессы и поймем, как мы можем использовать Flowable Java API для создания и развертывания примера бизнес-процесса.

2. Понимание бизнес-процессов

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

OMG (группа управления объектами) определила стандарт под названием «Модель и нотация бизнес-процессов» (BPMN) для предприятий, чтобы определять и сообщать о своих процессах. BPMN получила широкую поддержку и признание в отрасли. Flowable API полностью поддерживает создание и развертывание определений процессов BPMN 2.0.

3. Создание определений процесса

Предположим, у нас есть простой процесс проверки статьи перед ее публикацией.

Суть этого процесса заключается в том, что авторы представляют статью, а редакторы либо принимают, либо отклоняют ее. В случае принятия статья публикуется немедленно; однако в случае отклонения автор получает уведомление по электронной почте:

Мы создаем определения процессов в виде XML-файлов с использованием стандарта XML BPMN 2.0.

Давайте определим наш простой процесс в соответствии со стандартом BPMN 2.0:

<?xml version="1.0" encoding="UTF-8"?>
<definitions
    xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
    xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
    xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
    xmlns:flowable="http://flowable.org/bpmn"
    typeLanguage="http://www.w3.org/2001/XMLSchema"
    expressionLanguage="http://www.w3.org/1999/XPath"
    targetNamespace="http://www.flowable.org/processdef">
    <process id="articleReview"
      name="A simple process for article review." isExecutable="true">
        <startEvent id="start" />
        <sequenceFlow sourceRef="start" targetRef="reviewArticle" />
        <userTask id="reviewArticle" name="Review the submitted tutorial"
          flowable:candidateGroups="editors" />
        <sequenceFlow sourceRef="reviewArticle" targetRef="decision" />
        <exclusiveGateway id="decision" />
        <sequenceFlow sourceRef="decision" targetRef="tutorialApproved">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${approved}]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow sourceRef="decision" targetRef="tutorialRejected">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${!approved}]]>
            </conditionExpression>
        </sequenceFlow>
        <serviceTask id="tutorialApproved" name="Publish the approved tutorial."
          flowable:class="com.baeldung.service.PublishArticleService" />
        <sequenceFlow sourceRef="tutorialApproved" targetRef="end" />
        <serviceTask id="tutorialRejected" name="Send out rejection email"
          flowable:class="com.baeldung.service.SendMailService" />
        <sequenceFlow sourceRef="tutorialRejected" targetRef="end" />
        <endEvent id="end" />
    </process>
</definitions>

Здесь довольно много элементов, которые являются стандартными элементами XML, в то время как другие специфичны для BPMN 2.0:

    Весь Процесс заключен в тег под названием «процесс», который, в свою очередь, является частью тега под названием «определения». Процесс состоит из событий, потоков, задач и шлюзов. Событие может быть либо начальным, либо конечным событием. поток (в данном примере поток последовательности) соединяет другие элементы, такие как события и задачи. Задачи — это место, где выполняется реальная работа; среди прочего, это могут быть «пользовательские задачи» или «служебные задачи». Пользовательская задача требует, чтобы пользователь-человек взаимодействовал с Flowable API и выполнял действия. Служебная задача представляет собой автоматическую задачу, которая может быть вызовом класса Java. или даже HTTP-вызов, который шлюз выполняет на основе атрибута «утвержден»; это известно как переменная процесса, и мы увидим, как установить их позже

Хотя мы можем создавать файлы определения процесса в любом текстовом редакторе, это не всегда самый удобный способ. К счастью, Flowable также поставляется с параметрами пользовательского интерфейса, чтобы сделать это с помощью плагина Eclipse или веб-приложения. Если вместо этого вы используете IntelliJ, также доступен плагин IntelliJ.

4. Работа с Flowable API

Теперь, когда мы определили наш простой процесс в файле XML в соответствии со стандартом BPMN 2.0, нам нужен способ отправить и запустить его. Flowable предоставляет API Process Engine для взаимодействия с Flowable Engines. Flowable очень гибкий и предлагает несколько способов развертывания этого API.

Учитывая, что Flowable — это API Java, мы можем включить механизм обработки в любое приложение Java, просто включив необходимые файлы JAR. Мы можем очень хорошо использовать Maven для управления этими зависимостями.

Кроме того, Flowable поставляется со встроенными API для взаимодействия с Flowable через HTTP. Мы можем использовать эти API практически для всего, что возможно через Flowable API.

Наконец, Flowable отлично поддерживает интеграцию с Spring и Spring Boot! В нашем руководстве мы будем использовать интеграцию Flowable и Spring Boot.

5. Создание демонстрационного приложения с помощью Process Engine

Теперь давайте создадим простое приложение, которое является оболочкой для Process Engine из Flowable и предлагает API на основе HTTP для взаимодействия с Flowable API. Также может быть веб-приложение или мобильное приложение, работающее поверх API, чтобы улучшить работу, но мы пропустим это в этом руководстве.

Мы создадим нашу демонстрацию как приложение Spring Boot.

5.1. Зависимости

Во-первых, давайте посмотрим, какие зависимости нам нужно извлечь из Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.4.1</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

Все требуемые зависимости доступны в Maven Central:

    «Spring Boot Starter для Интернета — это стандартный пусковой механизм для Spring Boot Flowable Starter для Spring Boot — требуется для Spring Boot Flowable Engines База данных H2 — для Flowable требуется база данных для хранения данных, а H2 используется по умолчанию база данных в оперативной памяти

5.2. Определение процесса

Когда мы запускаем наше приложение Spring Boot, оно пытается автоматически загрузить все определения процессов, присутствующие в папке «resources/processes». Поэтому давайте создадим файл XML с определением процесса, которое мы создали выше, с именем «article-workflow.bpmn20.xml» и поместим его в эту папку.

5.3. Конфигурации

Поскольку мы знаем, что Spring Boot использует очень самоуверенный подход к конфигурации приложения, это справедливо и для Flowable как части Spring Boot. Например, обнаружив H2 как единственный драйвер базы данных в пути к классам, Flowable автоматически настраивает его для использования.

Очевидно, что каждый настраиваемый аспект можно настроить по-своему через свойства приложения. Однако для этого урока мы будем придерживаться значений по умолчанию!

5.4. Делегаты Java

В нашем определении процесса мы использовали пару классов Java, которые должны вызываться как части служебных задач. Эти классы реализуют интерфейс JavaDelegate и известны как делегаты Java в Flowable. Теперь мы определим фиктивные классы для этих Java-делегатов:

public class PublishArticleService implements JavaDelegate {
    public void execute(DelegateExecution execution) {
        System.out.println("Publishing the approved article.");
    }
}
public class SendMailService implements JavaDelegate {
    public void execute(DelegateExecution execution) {
        System.out.println("Sending rejection mail to author.");
    }
}

Очевидно, что мы должны заменить эти фиктивные классы реальными службами для публикации статьи или отправки электронной почты.

5.5. API-интерфейсы HTTP

Наконец, давайте создадим несколько конечных точек для взаимодействия с обработчиком процессов и работы с определенным нами процессом.

@RestController
public class ArticleWorkflowController {
    @Autowired
    private ArticleWorkflowService service;
 
    @PostMapping("/submit")
    public void submit(@RequestBody Article article) {
        service.startProcess(article);
    }
 
    @GetMapping("/tasks")
    public List<Article> getTasks(@RequestParam String assignee) {
        return service.getTasks(assignee);
    }
 
    @PostMapping("/review")
    public void review(@RequestBody Approval approval) {
        service.submitReview(approval);
    }
}

Мы начнем с определения контроллера, предоставляющего доступ к трем конечным точкам:

Наш контроллер предоставляет конечные точки для отправки статьи на проверку, получения списка статей для проверки и, наконец, отправки обзора статьи. . Статья и одобрение — это стандартные POJO, которые можно найти в репозитории.

@Service
public class ArticleWorkflowService {
    @Autowired
    private RuntimeService runtimeService;
 
    @Autowired
    private TaskService taskService;

    @Transactional
    public void startProcess(Article article) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("author", article.getAuthor());
        variables.put("url", article.getUrl());
        runtimeService.startProcessInstanceByKey("articleReview", variables);
    }
 
    @Transactional
    public List<Article> getTasks(String assignee) {
        List<Task> tasks = taskService.createTaskQuery()
          .taskCandidateGroup(assignee)
          .list();
        return tasks.stream()
          .map(task -> {
              Map<String, Object> variables = taskService.getVariables(task.getId());
              return new Article(task.getId(), (String) variables.get("author"), (String) variables.get("url"));
          })
          .collect(Collectors.toList());
    }
 
    @Transactional
    public void submitReview(Approval approval) {
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("approved", approval.isStatus());
        taskService.complete(approval.getId(), variables);
    }
}

На самом деле мы делегируем большую часть работы ArticleWorkflowService:

    Теперь большая часть кода здесь довольно интуитивно понятна, но давайте разберемся в основных моментах:

RuntimeService для создания экземпляра процесса для конкретной отправки TaskService для запроса и обновления задач. Обертывание всех вызовов базы данных в транзакциях, поддерживаемых Spring. они известны как переменные процесса, и мы можем получить к ним доступ в определении процесса, как мы видели ранее

Теперь мы готовы протестировать наше приложение и механизм процесса. Как только мы запустим приложение, мы можем просто использовать curl или любой клиент REST, такой как Postman, для взаимодействия с созданными нами конечными точками.

6. Процессы модульного тестирования

@ExtendWith(FlowableSpringExtension.class)
@ExtendWith(SpringExtension.class)
public class ArticleWorkflowUnitTest {
    @Autowired
    private RuntimeService runtimeService;
 
    @Autowired
    private TaskService taskService;
 
    @Test
    @Deployment(resources = { "processes/article-workflow.bpmn20.xml" })
    void articleApprovalTest() {
        Map<String, Object> variables = new HashMap<>();
        variables.put("author", "[email protected]");
        variables.put("url", "http://baeldung.com/dummy");
 
        runtimeService.startProcessInstanceByKey("articleReview", variables);
        Task task = taskService.createTaskQuery().singleResult();
 
        assertEquals("Review the submitted tutorial", task.getName());
 
        variables.put("approved", true);
        taskService.complete(task.getId(), variables);
 
        assertEquals(0, runtimeService.createProcessInstanceQuery().count());
    }
}

Flowable поддерживает различные версии JUnit, включая JUnit 5, для создания модульных тестов для бизнес-процессов. Потоковая интеграция со Spring также имеет подходящую поддержку для этого. Давайте посмотрим на типичный модульный тест для процесса в Spring:

Это должно быть очень похоже на стандартный модульный тест в Spring, за исключением нескольких аннотаций, таких как @Deployment. Теперь Flowable предоставляет аннотацию @Deployment для создания и удаления развертывания процесса вокруг методов тестирования.

7. Понимание развертывания процессов


Хотя мы не будем рассматривать детали развертывания процессов в этом руководстве, стоит рассмотреть некоторые важные аспекты.

Обычно процессы архивируются как бизнес-архив (BAR) и развертываются в приложении. При развертывании этот архив сканируется на наличие артефактов, таких как определения процессов, и обрабатывается. Возможно, вы заметили соглашение о том, что файл определения процесса заканчивается на «.bpmn20.xml».

«Хотя в нашем руководстве мы использовали базу данных H2 в памяти по умолчанию, ее на самом деле нельзя использовать в реальном приложении по той простой причине, что база данных в памяти не сохраняет никаких данных при запуске и практически невозможно использовать в кластерной среде! Следовательно, мы должны использовать реляционную базу данных производственного уровня и предоставлять необходимые конфигурации в приложении.

Хотя в самой BPMN 2.0 нет понятия управления версиями, Flowable создает атрибут версии для процесса, который развертывается в базе данных. Если развернута обновленная версия того же процесса, что идентифицируется атрибутом «id», создается новая запись с увеличенной версией. Когда мы пытаемся запустить процесс по «id», механизм процесса получает последнюю версию развернутого определения процесса.

Если мы воспользуемся одним из конструкторов, которые обсуждались ранее, для создания определения процесса, у нас уже будет визуализация нашего процесса. Мы можем экспортировать диаграмму процесса в виде изображения и поместить ее вместе с XML-файлом определения процесса. Если мы будем придерживаться стандартного соглашения об именах, предложенного Flowable, это изображение будет обрабатываться механизмом процесса вместе с самим процессом. Более того, мы можем получить это изображение и через API!

8. Просмотр истории экземпляров процесса

В случае с бизнес-процессами очень важно понять, что произошло в прошлом. Нам это может понадобиться для простой отладки или сложного юридического аудита.

Flowable записывает, что происходит во время выполнения процесса, и хранит его в базе данных. Более того, Flowable делает эту историю доступной через API для запроса и анализа. Есть шесть сущностей, в которых Flowable записывает их, и у HistoryService есть методы для запроса их всех.

HistoryService historyService = processEngine.getHistoryService();
List<HistoricActivityInstance> activities = historyService
  .createHistoricActivityInstanceQuery()
  .processInstanceId(processInstance.getId())
  .finished()
  .orderByHistoricActivityInstanceEndTime()
  .asc()
  .list();

Давайте рассмотрим простой запрос для извлечения завершенных экземпляров процесса:

Как мы видим, API для запроса записанных данных довольно компонуем. В этом примере мы запрашиваем завершенные экземпляры процесса по идентификатору и упорядочиваем их в порядке возрастания времени окончания.

9. Мониторинг процессов

Мониторинг является ключевым аспектом любого критически важного для бизнеса приложения и тем более приложения, управляющего бизнес-процессами организации. Flowable имеет несколько опций, позволяющих нам отслеживать процессы в режиме реального времени.

Flowable предоставляет определенные MBean-компоненты, к которым мы можем получить доступ через JMX, не только для сбора данных для мониторинга, но и для выполнения многих других действий. Мы можем интегрировать это с любым стандартным клиентом JMX, включая jconsole, который присутствует вместе со стандартными дистрибутивами Java.

Использование JMX для мониторинга открывает много возможностей, но является относительно сложным и требует много времени. Однако, поскольку мы используем Spring Boot, нам повезло!

Spring Boot предлагает конечные точки Actuator для сбора метрик приложений по HTTP. Мы можем легко интегрировать это со стеком инструментов, таким как Prometheus и Grafana, чтобы создать инструмент мониторинга производственного уровня с минимальными усилиями.

Flowable предоставляет дополнительную конечную точку актуатора, предоставляющую информацию о запущенных процессах. Это не так хорошо, как сбор информации через JMX, но это быстро, просто и, самое главное, достаточно.

10. Заключение

В этом руководстве мы обсудили бизнес-процессы и то, как их определить в стандарте BPMN 2.0. Затем мы обсудили возможности Flowable Process Engine и API для развертывания и выполнения процессов. Мы увидели, как интегрировать это в приложение Java, в частности, в Spring Boot.

Продолжая дальше, мы обсудили другие важные аспекты процессов, такие как их развертывание, визуализация и мониторинг. Излишне говорить, что мы только что поцарапали поверхность бизнес-процесса и такого мощного механизма, как Flowable. Flowable имеет очень богатый API с достаточной доступной документацией. Этот урок, однако, должен был пробудить наш интерес к предмету!