Working with BPMN in PROCEED ‐ bpmn‐js, diagram‐js, bpmn‐moddle - PROCEED-Labs/proceed GitHub Wiki
To create, modify and interpret BPMN in the PROCEED project we use multiple different tools. Multiple of these are provided by the bpmn.io toolkit (Here is their website and their GitHub page). Information on how these different libraries interact with one another can be found in the bpmn-js walktrough. Different examples on how to work with bpmn-js and how to extend it can be found here.
Since PROCEED is all about BPMN model creation and execution, it is essential to understand how to use these tools so we will give a short introduction to the most important libraries that are used.
bpmn.js
In the Management System we use the Modeler to graphically edit BPMN diagrams and for Viewers to display BPMN models. Both is provided by the bpmn.js
library.
bpmn-js
is build on top of diagram-js
and bpmn-moddle
.
diagram.js
diagram-js
provides general functionality for graphically interacting with different kinds of diagrams by offering modules.
It has multiple ways (i.e. modules) of extending or replacing the base functionality, which is how bpmn-js
transforms it into a specialized BPMN modeling and displaying library.
Since our own extensions and custom modeling behavior will have to be implemented by using the same functionality, it is useful to know some of the most important _modules _provided by diagram-js
which are used heavily in bpmn-js
.
To access a module you can use modeler.get([module-name])
. In the following, we will list and explain the most important modules.
Other modules can be found in the core
, features
and draw
directories in diagram-js and bpmn-js. To learn how to create custom modules and how to overwrite base modules take a look at the aforementioned examples
eventBus
The eventBus is used to register custom logic for different events that are triggered during modeling, e.g., when element is added or is removed.
Here is an example where a callback is registered that is run every time a user selects one or multiple elements in the modeler:
const eventBus = modeler.get('eventBus');
eventBus.on('selection.changed', ({ newSelection }) => {
// do something with the new selection
});
bpmn-js
provides a shortcut with modeler.on([event-name], [callback]);
commandStack
The commandStack is responsible for handling any kind of model change, like adding or removing flow elements. It will trigger multiple life cycle events during the execution of a model change that allow extensions to hook into the change execution. For all the different life cycle events, take a look at the comments in the linked file.
Here is an example where a callback is registered that is run every time after an element has been added:
const eventBus = modeler.get('eventBus');
eventBus.on('commandStack.shape.create.postExecuted', ({ context: { shape }}) => {
// do something with the new shape
});
The commandStack also provides undo and redo functionality with modeler.get('commandStack').undo()
and modeler.get('commandStack').redo()
.
elementRegistry
The elementRegistry provides easy access by id to all elements that exist in the current model by doing modeler.get('elementRegistry').get([element-id]);
diagram-js and bpmn-js)
modeling (withThe modeling module can be used to trigger model changes programmatically. bpmn-js
overwrites the base module with its own implementation, which inherits all the functionality from the base module. If you want to know which modeling events exist, you can take a look at the handler names in the files and then add commandStack and the respective life cycle phase (e.g. commandStack.[handler-name].preExecute
).
Here is an example how to trigger a model change using the modeling module (we change the name of an element, which can be seen as its label in the modeler)
const modeling = modeler.get('modeling');
const elementRegistry = modeler.get('elementRegistry');
const element = elementRegistry.get([element-id]);
modeling.updateProperties(element, { name: 'new name' });
bpmn-moddle
To parse information from BPMN data or even modify BPMN programmatically, we use bpmn-moddle
.
This library is also used inside bpmn-js
for parsing and serializing the edited BPMN and to create BPMN elements.
bpmn-moddle builds on top of moddle
and moddle-xml
.
With moddle
one can define meta models using JSON, which can then be used to create elements, validate existing models and files.
moddle-xml
provides a parser and serializer for XML-based models with support through the moddle
library.
bpmn-moddle
provides a meta model with all the default BPMN elements, which is used when parsing and serializing BPMN and when creating BPMN elements.
bpmn-moddle
is used both, in the Engine and the Management System, when BPMN has to be edited or interpreted outside of the modeler.
Custom BPMN elements
To support our use cases, we also need to make use of BPMNs extensibility by defining custom elements or attributes for existing elements.
bpmn-moddle
allows us to do that by providing a custom model when creating an instance of it (const bpmnModdle = new BpmnModdle({proceed: customSchema});
). To see some examples of how to define custom elements you can read the page in the moddle
library or take a look at the BPMN schema or our custom schema.
We need to provide the extension to the modeler as well.
this.modeler = new BpmnModeler({
container: '#canvas',
moddleExtensions: {
proceed: customSchema,
},
});
helper module
We have created a helper module that provides some functions that wrap bpmn-moddle
functionality. It also contains a JSON file with all our model extensions.
The most generally useful functionality provided by the helper module can be found in the util.js
file.
The other files contain more specialized functions that might be useful in specific situations.