MODEL
You can use Camunda Modeler of bpmn.io with BPE:

SESSION
Transactional persistent (rocksdb) business processing with multi-threading executioin, hierarchical traces and BPMN 2.0 XML support out of the box.
iex(1)> {_,p} = :bpe.start :bpe_xml.def, []
{:ok, "77012724426000"}
iex(2)> :bpe.next p
{:complete, 'either'}
iex(3)> :bpe.next p
{:complete, 'left'}
iex(4)> :bpe.next p
{:complete, 'right'}
iex(5)> :bpe.next p
{:complete, 'join'}
iex(6)> :bpe.next p
{:complete, 'epilog'}
iex(7)> :bpe.next p
{:complete, 'finish'}
iex(8)> :bpe.next p
:Final
iex(10)> :kvs.all :writer
[
{:writer, '/bpe/proc', 1, [], [], []},
{:writer, '/bpe/flow/77012724426000', 1, [], [], []},
{:writer, '/bpe/hist/77012724426000', 1, [], [], []}
]
iex(11)> :bpe.hist p
[
{:hist, {:step, 7, '77012724426000'}, :feed, [], [], [], [],
{:sequenceFlow, 'x7', [], 'epilog', 'finish'}, [],
{:ts, {{2019, 10, 11}, {4, 3, 7}}}},
{:hist, {:step, 6, '77012724426000'}, :feed, [], [], [], [],
{:sequenceFlow, 'x6', [], 'join', 'epilog'}, [],
{:ts, {{2019, 10, 11}, {4, 3, 6}}}},
{:hist, {:step, 5, '77012724426000'}, :feed, [], [], [], [],
{:sequenceFlow, 'x5', [], 'right', 'join'}, [],
{:ts, {{2019, 10, 11}, {4, 3, 6}}}},
{:hist, {:step, 4, '77012724426000'}, :feed, [], [], [], [],
{:sequenceFlow, 'x4', [], 'left', 'join'}, [],
{:ts, {{2019, 10, 11}, {4, 3, 6}}}},
{:hist, {:step, 3, '77012724426000'}, :feed, [], [], [], [],
{:sequenceFlow, 'x3', [], 'either', 'right'}, [],
{:ts, {{2019, 10, 11}, {4, 3, 5}}}},
{:hist, {:step, 2, '77012724426000'}, :feed, [], [], [], [],
{:sequenceFlow, 'x2', [], 'either', 'left'}, [],
{:ts, {{2019, 10, 11}, {4, 3, 5}}}},
{:hist, {:step, 1, '77012724426000'}, :feed, [], [], [], [],
{:sequenceFlow, 'x1', [], 'start', 'either'}, [],
{:ts, {{2019, 10, 11}, {4, 3, 4}}}},
{:hist, {:step, 0, '77012724426000'}, :feed, [], [], [], [], :Created, [],
{:ts, {{2019, 10, 11}, {4, 2, 25}}}}
]
> :kvs.all '/bpe/flow/77012724426000'
[
{:sched, {:step, 0, '77012724426000'}, 1, ['x1']},
{:sched, {:step, 1, '77012724426000'}, 1, ['x2', 'x3']},
{:sched, {:step, 2, '77012724426000'}, 2, ['x4', 'x3']},
{:sched, {:step, 3, '77012724426000'}, 1, ['x4', 'x5']},
{:sched, {:step, 4, '77012724426000'}, 1, ['x5']},
{:sched, {:step, 5, '77012724426000'}, 1, ['x6']},
{:sched, {:step, 6, '77012724426000'}, 1, ['x7']},
{:sched, {:step, 7, '77012724426000'}, 1, []}
]
BPMN SPEC
This schema covers 80% of all workflows and provides only 20% of standard coverage. We call it Lightweight BPMN or BPE.
-record(beginEvent , { ?TASK }).
-record(endEvent, { ?TASK }).
-record(task, { ?TASK }).
-record(userTask, { ?TASK }).
-record(serviceTask, { ?TASK }).
-record(receiveTask, { ?TASK, reader=[] :: #reader{} }).
-record(sendTask, { ?TASK, writer=[] :: #writer{} }).
-record(gateway, { ?TASK, type= parallel :: gate(), filler=[] :: [] }).
-record(messageEvent, { ?EVENT }).
-record(boundaryEvent, { ?EVENT, ?CYCLIC }).
-record(timeoutEvent, { ?EVENT, ?CYCLIC }).
-record(timeout, { spec= [] :: term() }).
-record(sequenceFlow, { name=[] :: term(),
condition=[] :: term(),
source=[] :: [] | atom(),
target=[] :: [] | atom() | list(atom()) }).
-type events() :: #messageEvent{} | #boundaryEvent{} | #timeoutEvent{}.
-type procId() :: [] | integer() | {atom(),any()}.
-type gate() :: exclusive | parallel | inclusive | complex | event.
-type tasks() :: #task{} | #serviceTask{} | #userTask{}
| #receiveTask{} | #beginEvent{} | #endEvent{}.
BPE API
-record(ts, { time= [] :: term() }).
-record(step, { id = 0 :: integer(), proc = "" :: list() }).
-record(sched, { id = [] :: [] | #step{},
pointer = -1 :: integer(),
state = [] :: list(#sequenceFlow{}) }).
-record(hist, { id = [] :: [] | #step{},
name=[] :: [] | binary(),
task=[] :: [] | atom(),
docs=[] :: list(tuple()),
time=[] :: [] | #ts{} }).
-record(process, { id = [] :: procId(),
name=[] :: [] | binary() | string() | atom(),
feeds=[] :: list(),
roles=[] :: list(),
tasks=[] :: list(tasks()),
events=[] :: list(events()),
flows = [] :: list(#sequenceFlow{}),
rules = [] :: [] | term(),
docs = [] :: list(tuple()),
options = [] :: term(),
task = 'Created' :: [] | atom(),
timer = [] :: [] | reference(),
notifications=[] :: [] | term(),
result = [] :: [] | binary(),
started = [] :: [] | #ts{},
beginEvent = [] :: [] | atom(),
endEvent = [] :: [] | atom()}).
-record(subProcess, { name=[] :: [] | atom(),
diagram= #process{} :: #process{} }).
BANK SAMPLE
-module(bpe_account).
-include("bpe.hrl").
-compile(export_all).
action(_,P) ->
{reply,P}.
def() ->
#process { name = 'IBAN Account',
flows = [
#sequenceFlow{name='1', source='Created', target='Init'},
#sequenceFlow{name='2', source='Init', target='Upload'},
#sequenceFlow{name='3', source='Upload', target='Payment'},
#sequenceFlow{name='4', source='Payment', target='Signatory'},
#sequenceFlow{name='5', source='Payment', target='Process'},
#sequenceFlow{name='6', source='Process', target='Process'},
#sequenceFlow{name='7', source='Process', target='Final'},
#sequenceFlow{name='8', source='Signatory', target='Process'},
#sequenceFlow{name='9', source='Signatory', target='Final'} ],
tasks = [
#beginEvent { name='Created', module = bpe_account },
#userTask { name='Init', module = bpe_account },
#userTask { name='Upload', module = bpe_account },
#userTask { name='Signatory', module = bpe_account },
#serviceTask { name='Payment', module = bpe_account },
#serviceTask { name='Process', module = bpe_account },
#endEvent { name='Final', module = bpe_account } ],
beginEvent = 'Created',
endEvent = 'Final',
events = [#messageEvent { name='PaymentReceived'},
#boundaryEvent{ name='*',
timeout=#timeout{spec={0,{10,0,10}}}}]}.
PRESENCE
Companies that use BPE:
- pb.ua/depozit — АТ КБ "ПриватБанк"
- nynja.io — NYNJA, Inc.
- infotech.gov.ua — ДП "ІНФОТЕХ".