Fluent Builder API
|
Этот раздел перенесён из документации Camunda 7 и в дальнейшем будет доработан с учётом особенностей OpenBPM Engine |
Чтобы создавать простые BPMN процессы, мы предоставили fluent builder API. С этим API вы можете легко создавать базовые процессы в несколько строк кода. В демо для быстрого начала работы с fluent API мы показываем, как создать довольно сложный процесс с пятью задачами и двумя шлюзами, использовав всего около 50 строк кода.
Fluent Builder API еще весьма далек от завершения, но уже предлагает вам следующие базовые элементы:
-
процесс
-
событие старта
-
исключающий шлюз
-
параллельный шлюз
-
скриптовая задача
-
сервисная задача
-
пользовательская задача
-
определение сигнального события
-
событие окончания
-
подпроцесс
Создание процесса с помощью Fluent Builder API
Чтобы создать пустой экземпляр модели с новым процессом, используется метод Bpmn.createProcess(). После этого вы можете добавить сколько угодно задач и шлюзов. В конце вы должны вызвать done(), чтобы вернуть сгенерированный экземпляр модели. Например, простой процесс с одной пользовательской задачей может быть создан вот так:
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.userTask()
.endEvent()
.done();
Чтобы добавить новый элемент, вам придется вызвать функцию, которая имеет такое же имя, как и добавляемый элемент. Дополнительно вы можете установить атрибуты последнего созданного элемента.
Например, давайте установим имя процесса и пометим его как выполняемый, а также дадим имя пользовательской задаче.
BpmnModelInstance modelInstance = Bpmn.createProcess()
.name("Example process")
.executable()
.startEvent()
.userTask()
.name("Some work to do")
.endEvent()
.done();
Как вы можете видеть, последовательный процесс действительно очень простой и моделируется элементарным образом, но зачастую вам бывают нужны ветвления и параллельные пути выполнения, что тоже может быть сделано с помощью Fluent Builder API. Просто добавьте параллельный или исключающий шлюз и смоделируйте первый путь до конца события ил до следующего шлюза. После этого вызовите метод moveToLastGateway(), и вы вернетесь к последнему шлюзу, откуда вы сможете смоделировать следующий путь.
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.userTask()
.parallelGateway()
.scriptTask()
.endEvent()
.moveToLastGateway()
.serviceTask()
.endEvent()
.done();
Этот пример моделирует процесс с пользовательской задачей после события старта, за которой следует параллельный шлюз с двумя параллельниыми исходящими путями выполнения, каждый со своей задачей и своим событием.
В нормальной ситуации вы захотели бы добавить условия на исходящие потоки исключающего шлюза, что тоже легко создать с помощью Fluent Builder API. Просто используйте метод condition() и дайте ему метку и выражение.
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.userTask()
.exclusiveGateway()
.name("What to do next?")
.condition("Call an agent", "#{action = 'call'}")
.scriptTask()
.endEvent()
.moveToLastGateway()
.condition("Create a task", "#{action = 'task'}")
.serviceTask()
.endEvent()
.done();
Если вы хотите использовать метод moveToLastGateway(), но у вас несколько входящих потоков последовательностей в вашей текущей позиции, вам придется использовать generic метод moveToNode с id шлюза. Это может произойти, например, если вы добавляете шлюз типа join к вашему процессу. Для этой цели, как и для циклов, мы добавили метод connectTo(elementId).
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.userTask()
.parallelGateway("fork")
.serviceTask()
.parallelGateway("join")
.moveToNode("fork")
.userTask()
.connectTo("join")
.moveToNode("fork")
.scriptTask()
.connectTo("join")
.endEvent()
.done()
Этот пример создает процесс с тремя параллельными путями выполнения, которые все объединяются на втором шлюзе. Отметим, что первый вызов moveToNode не является необходимым, поскольку в этой точке объединяющий шлюз имеет только один входящий поток последовательности, но этот первый вызов был испооьзован для единообразия.
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.userTask()
.id("question")
.exclusiveGateway()
.name("Everything fine?")
.condition("yes", "#{fine}")
.serviceTask()
.userTask()
.endEvent()
.moveToLastGateway()
.condition("no", "#{!fine}")
.userTask()
.connectTo("question")
.done()
Этот пример создает параллельный шлюз с обратной петлей на втором пути выполнения.
Чтобы создать встроенный подпроцесс с помощью Fluent Builder API, вы можете добавить его напрямую при построении процесса или отсоединить его и создать элементы потока подпроцесса позднее.
// Directly define the subprocess
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.subProcess()
.camundaAsync()
.embeddedSubProcess()
.startEvent()
.userTask()
.endEvent()
.subProcessDone()
.serviceTask()
.endEvent()
.done();
// Detach the subprocess building
modelInstance = Bpmn.createProcess()
.startEvent()
.subProcess("subProcess")
.serviceTask()
.endEvent()
.done();
SubProcess subProcess = (SubProcess) modelInstance.getModelElementById("subProcess");
subProcess.builder()
.camundaAsync()
.embeddedSubProcess()
.startEvent()
.userTask()
.endEvent();
Нижеприведенный пример показывает, как создать определение события выбрасывания сигнала и задать полезную нагрузку, которую будет содержать этот сигнал. Используя camundaIn методы, можно задать, какие процессные переменные будут включены в полезную нагрузку сигнала, задать выражение, которое будет разрешено в экземплярах процессов, принимающих сигнал или объявить, что все процессные переменные в экземпляре подающего сигнал процесса должны передаваться. Можно также задать бизнес-ключ, который будет назначен экземплярам процессов, принимающих сигнал.
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.intermediateThrowEvent("throw")
.signalEventDefinition("signal")
.camundaInSourceTarget("source", "target1")
.camundaInSourceExpressionTarget("${'sourceExpression'}", "target2")
.camundaInAllVariables("all", true)
.camundaInBusinessKey("aBusinessKey")
.throwEventDefinitionDone()
.endEvent()
.done();
Расширение процесса с помощью Fluent Builder API
С помощью Fluent Builder API вы можете не только создавать процессы, но и расширять существующие.
Например, представьте себе процесс, содержащий параллельный шлюз с id равным значению gateway. Теперь вы хотите добавить к нему другой путь выполнения ради новой сервисной задачи, которая должна выполняться каждый раз.
BpmnModelInstance modelInstance = Bpmn.readModelFromFile(new File("PATH/TO/MODEL.bpmn"));
ParallelGateway gateway = (ParallelGateway) modelInstance.getModelElementById("gateway");
gateway.builder()
.serviceTask()
.name("New task")
.endEvent();
Другой сценарий использования — это вставить новые задачи между существующими элементами. Представьте себе процесс, содержащий пользовательскую задачу с id, равным task1, за которой следует сервисная задача. Теперь вы хотите добавить скриптовую задачу и пользовательскую задачу между этими двумя.
BpmnModelInstance modelInstance = Bpmn.readModelFromFile(new File("PATH/TO/MODEL.bpmn"));
UserTask userTask = (UserTask) modelInstance.getModelElementById("task1");
SequenceFlow outgoingSequenceFlow = userTask.getOutgoing().iterator().next();
FlowNode serviceTask = outgoingSequenceFlow.getTarget();
userTask.getOutgoing().remove(outgoingSequenceFlow);
userTask.builder()
.scriptTask()
.userTask()
.connectTo(serviceTask.getId());
Распространенные паттерны моделей
Контроль за границами транзакций
Транзакционные границы процесса, созданного при помощи Fluent Builder API, могут контролироваться с использованием методов camundaAsyncBefore() и camundaAsyncAfter(), предлагаемых для различных конструктов процесса.
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.serviceTask("servicetask")
.camundaAsyncBefore()
.userTask("task")
.camundaAsyncAfter()
.done();
Сервисная задача в приведенном выше примере получить транзакционные границы до своего выполнения, а пользовательская задача получит транзакционные границы после своего завершения.
Если у активности присутствуют характеристики наличия множественных экземпляров, методы camundaAsyncBefore() и camundaAsyncAfter() применяются к мультиэкземплярному телу как к единому целому. Транзакционные границы индивидуальных появлений (экземпляров) мультиэкземплярности могут управляться чрез подобные методы, которые вызываются изнутри мультиэкземплярного билдера.
BpmnModelInstance modelInstance = Bpmn.createProcess()
.startEvent()
.serviceTask("servicetask")
.camundaAsyncBefore() // multi-instance body
.multiInstance()
.camundaAsyncBefore() // every instance
.parallel()
.multiInstanceDone()
.endEvent()
.done();
Генерация развязки диаграммы
Для рендеринга процесса необходимы элементы BPMN-диаграммы. Fluent Builder генерирует BPMN Shapes (геометрические формы) и BPMN Edges (края, грани) и размещает их автоматически для использования в узлах потоков и в потоках последовательностей.
final BpmnModelInstance myProcess = Bpmn.createExecutableProcess("process-payments")
.startEvent()
.serviceTask()
.name("Process Payment")
.endEvent()
.done();
System.out.println(Bpmn.convertToString(myProcess));
Этот пример создает BPMN, содержащую как семантические элементы (например, сервисную задачу), так и элементы диаграммы:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<definitions xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="definitions_dfb1f18e-6034-448e-abae-0eb2f41469da" targetNamespace="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
<!-- Generated BPMN Semantic Elements -->
<process id="process-payments" isExecutable="true">
<startEvent id="startEvent_2b0abd37-75a9-47dd-9838-63f1390d7515">
<outgoing>sequenceFlow_b1eec5b5-889d-4e75-854d-59768fbdc8a2</outgoing>
</startEvent>
<serviceTask id="serviceTask_f4c2413f-5b26-49e8-b71c-2603e469ce09" name="Process Payment">
<incoming>sequenceFlow_b1eec5b5-889d-4e75-854d-59768fbdc8a2</incoming>
<outgoing>sequenceFlow_5839394a-c0c2-4a5b-aa81-9412f169cc35</outgoing>
</serviceTask>
<sequenceFlow id="sequenceFlow_b1eec5b5-889d-4e75-854d-59768fbdc8a2" sourceRef="startEvent_2b0abd37-75a9-47dd-9838-63f1390d7515" targetRef="serviceTask_f4c2413f-5b26-49e8-b71c-2603e469ce09"/>
<endEvent id="endEvent_8087f927-a53b-4126-95fc-c057736f3b73">
<incoming>sequenceFlow_5839394a-c0c2-4a5b-aa81-9412f169cc35</incoming>
</endEvent>
<sequenceFlow id="sequenceFlow_5839394a-c0c2-4a5b-aa81-9412f169cc35" sourceRef="serviceTask_f4c2413f-5b26-49e8-b71c-2603e469ce09" targetRef="endEvent_8087f927-a53b-4126-95fc-c057736f3b73"/>
</process>
<!-- Generated Diagram Elements -->
<bpmndi:BPMNDiagram id="BPMNDiagram_5b66dfb7-097b-4610-9681-43abb3ff97da">
<bpmndi:BPMNPlane bpmnElement="process-payments" id="BPMNPlane_ad88b4cf-9d7a-4b86-8386-f8db23ff388d">
<bpmndi:BPMNShape bpmnElement="startEvent_2b0abd37-75a9-47dd-9838-63f1390d7515" id="BPMNShape_d6c4e3c5-150c-43f7-adf8-1d388f466a69">
<dc:Bounds height="36.0" width="36.0" x="100.0" y="100.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="serviceTask_f4c2413f-5b26-49e8-b71c-2603e469ce09" id="BPMNShape_51006773-13df-4327-a4cf-a5952c39e86a">
<dc:Bounds height="80.0" width="100.0" x="186.0" y="78.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_b1eec5b5-889d-4e75-854d-59768fbdc8a2" id="BPMNEdge_fb360594-8863-4d5d-b515-49e02a88d55d">
<di:waypoint x="136.0" y="118.0"/>
<di:waypoint x="186.0" y="118.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="endEvent_8087f927-a53b-4126-95fc-c057736f3b73" id="BPMNShape_23930820-5507-45a0-8630-b5e45ee8dd4d">
<dc:Bounds height="36.0" width="36.0" x="336.0" y="100.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_5839394a-c0c2-4a5b-aa81-9412f169cc35" id="BPMNEdge_07ed502e-069f-42a0-bd1b-fed0d68efbda">
<di:waypoint x="286.0" y="118.0"/>
<di:waypoint x="336.0" y="118.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
Поведение по умолчанию состоит в том, что каждый вновь добавляемый элемент потока будет размещен рядом с предыдущим элементом потока.
Когда элементы потока добавляются к встроенному подпроцессу, тогда у этого подпроцесса автоматически меняется размер, когда достигается его граница. Поэтому, мы рекомендуем сначала добавить новые элементы к подпроцессу, и затем создавать новые элементы. Иначе это можнт привести к накладыванию элементов в диаграмме друг на друга.
Ветви, отходящие от шлюзов, помещаются одна над другой. Функция автоматического оптимального размещения не предоставляется, поэтому элементы, принадлежащие к различным ветвям, могут накладываться.
Лицензия и атрибуция
Эта документация была создана на базе материала "Camunda 7 Docs" от Camunda, находится под лицензией Creative Commons Attribution-ShareAlike 3.0 Unported License .
Оригинал документации: https://docs.camunda.org