{"id":13550649,"url":"https://github.com/synrc/bpe","last_synced_at":"2025-05-16T11:04:34.290Z","repository":{"id":14046777,"uuid":"16749477","full_name":"synrc/bpe","owner":"synrc","description":"💠 BPE: BPMN Process Engine ISO 19510","archived":false,"fork":false,"pushed_at":"2025-05-14T17:22:07.000Z","size":1878,"stargazers_count":270,"open_issues_count":1,"forks_count":67,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-05-14T18:22:26.757Z","etag":null,"topics":["bpmn","processing","transactions","workflow"],"latest_commit_sha":null,"homepage":"https://bpe.n2o.dev","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/synrc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2014-02-11T23:28:29.000Z","updated_at":"2025-05-14T17:22:10.000Z","dependencies_parsed_at":"2025-04-19T09:21:35.036Z","dependency_job_id":"75a6cb3a-f6a0-4740-b44c-52598ae9f2a3","html_url":"https://github.com/synrc/bpe","commit_stats":{"total_commits":790,"total_committers":19,"mean_commits":"41.578947368421055","dds":"0.28101265822784816","last_synced_commit":"70398fe470ec812b993dba8d80c6f200e67e6e07"},"previous_names":["spawnproc/bpe"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/synrc%2Fbpe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/synrc%2Fbpe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/synrc%2Fbpe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/synrc%2Fbpe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/synrc","download_url":"https://codeload.github.com/synrc/bpe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254405303,"owners_count":22065854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bpmn","processing","transactions","workflow"],"created_at":"2024-08-01T12:01:35.758Z","updated_at":"2025-05-16T11:04:34.269Z","avatar_url":"https://github.com/synrc.png","language":"Erlang","readme":"BPE: Business Process Engine\n============================\n\n[![Actions Status](https://github.com/synrc/bpe/workflows/mix/badge.svg)](https://github.com/synrc/bpe/actions)\n[![Hex pm](https://img.shields.io/hexpm/v/bpe.svg?style=flat)](https://hex.pm/packages/bpe)\n\nOverview\n--------\n\nBPE is a Business Process Engine that brings BPMN to Erlang and Erlang to Enterprises.\nIt provides infrastructure for Workflow Definitions, Process Orchestration,\nRule Based Production Systems and Distributed Storage.\n\n\u003cimg src=\"https://bpe.n2o.dev/man/img/camunda.png\" width=600/\u003e\n\n```\n\n iex(1)\u003e {_,p} = :bpe.start :bpe_xml.def, []\n {:ok, '76900759556000'}\n iex(2)\u003e :bpe.next p\n {:complete, 'either'}\n iex(3)\u003e :bpe.next p\n {:complete, 'left'}\n iex(4)\u003e :bpe.next p\n {:complete, 'right'}\n iex(5)\u003e :bpe.next p\n {:complete, 'join'}\n iex(6)\u003e :bpe.next p\n {:complete, 'join'}\n iex(7)\u003e :bpe.next p\n {:complete, 'epilog'}\n iex(8)\u003e :bpe.next p\n {:complete, 'finish'}\n iex(9)\u003e :bpe.next p\n :Final\n```\n\n```\n\u003e :kvs.all '/bpe/flow/77012724426000'\n [\n   {:sched, {:step, 0, '77012724426000'}, 1, ['x1']},\n   {:sched, {:step, 1, '77012724426000'}, 1, ['x2', 'x3']},\n   {:sched, {:step, 2, '77012724426000'}, 2, ['x4', 'x3']},\n   {:sched, {:step, 3, '77012724426000'}, 1, ['x4', 'x5']},\n   {:sched, {:step, 4, '77012724426000'}, 1, ['x5']},\n   {:sched, {:step, 5, '77012724426000'}, 1, ['x6']},\n   {:sched, {:step, 6, '77012724426000'}, 1, ['x7']},\n   {:sched, {:step, 7, '77012724426000'}, 1, []}\n ]\n```\n\nProcesses\n---------\n\nProcesses are main entities of BPE, they map one-to-one to Erlang processes.\nBasically, BPE process is an algorithm or function, that is executed entirely in the\ncontext of Erlang process. The arguments for such algorithms are:\nvalues from infinite streams (KVS chains);\nvalues from Erlang messages being sent to BPE process.\n\n```erlang\n-record(step,  { id = 0 :: integer(), proc = \"\" :: list() }).\n\n-record(role,  { id = [] :: list(), name :: binary(), tasks = [] :: term() }).\n\n-record(sched, { id = [] :: [] | #step{},\n                 prev=[] :: [] | integer(),\n                 next=[] :: [] | integer(),\n                 pointer = -1 :: integer(),\n                 state = [] :: list(list()) }).\n                 \n-record(hist,         { id = [] :: [] | #step{},\n                        prev=[] :: [] | integer(),\n                        next=[] :: [] | integer(),\n                        name=[] :: [] | binary() | list(),\n                        task=[] :: [] | atom() | list() | #sequenceFlow{} | condition(),\n                        docs=[] :: list(tuple()),\n                        time=[] :: [] | #ts{} }).\n\n-record(process,      { id = [] :: procId(),\n                        prev=[] :: [] | integer(),\n                        next=[] :: [] | integer(),\n                        name=[] :: [] | binary() | string() | atom(),\n                        feeds=[] :: list(),\n                        roles      = [] :: term(),\n                        tasks      = [] :: list(tasks()),\n                        events     = [] :: list(events()),\n                        flows      = [] :: list(#sequenceFlow{}),\n                        docs       = [] :: list(tuple()),\n                        options    = [] :: term(),\n                        module     = ?DEFAULT_MODULE :: [] | atom(),\n                        xml        = [] :: list(),\n                        timer      = [] :: [] | reference(),\n                        notifications=[] :: [] | term(),\n                        result     = [] :: [] | binary(),\n                        started    = [] :: [] | #ts{},\n                        beginEvent = [] :: list() | atom(),\n                        endEvent   = [] :: list() | atom() }).\n```\n\nDuring execution of the process, all steps are being written to the persistent storage,\nby which execution logic is restorable and reproducible. The process definition is actually\ndiagram or graph where points represented by `task` and edges by `sequenceFlow`.\n\nTasks and Flows\n---------------\n\nThe step itself is represented as `task` (point). The transition between steps is\nrepresented as `sequenceFlow` (edge). \n\n```erlang\n-define(TASK,           id=[] :: list(),\n                        name=[] :: list() | binary(),\n                        in=[] :: list(list()),\n                        out=[] :: list(list()),\n                        prompt=[] :: list(tuple()),\n                        roles=[] :: list(atom()),\n                        etc=[] :: list({term(),term()}) ).\n\n-record(beginEvent ,  { ?TASK }).\n-record(endEvent,     { ?TASK }).\n-record(task,         { ?TASK }).\n-record(userTask,     { ?TASK }).\n-record(serviceTask,  { ?TASK }).\n-record(receiveTask,  { ?TASK, reader=[] :: #reader{} }).\n-record(sendTask,     { ?TASK, writer=[] :: #writer{} }).\n```\n\nThe history record of process execution is\nrepresented as `hist` and captures the `sequenceFlow` information.\n\n```erlang\n-type condition() :: {compare,BpeDocParam :: \n                         { atom(),\n                           term()},\n                           Field :: integer(),\n                           ConstCheckAgainst :: term()\n                         }\n                   | {service,atom()}.\n\n-record(sequenceFlow, { id=[] :: list(),\n                        name=[] :: list() | binary(),\n                        condition=[] :: [] | condition() | binary(),\n                        source=[] :: list(),\n                        target=[] :: list(integer()) | list(list()) }).\n```\n\nEvents\n------\n\nWhile Tasks are deterministic, where you're getting a new task from previous one,\nthe Events are non-deterministic, where you could get a new task by external\nevent from the system to the process.\n\n```erlang\n-define(EVENT,          id=[] :: list() | atom(),\n                        name=[] :: list() | binary(),\n                        prompt=[] :: list(tuple()),\n                        etc=[] :: list({term(),term()}),\n                        payload=[] :: [] | binary(),\n                        timeout=[] :: [] | #timeout{} ).\n\n-define(CYCLIC,         timeDate=[] :: [] | binary(),\n                        timeDuration=[] :: [] | binary(),\n                        timeCycle=[] :: [] | binary() ).\n\n-record(messageEvent, { ?EVENT }).\n-record(messageBeginEvent, { ?EVENT }).\n-record(boundaryEvent,{ ?EVENT, ?CYCLIC }).\n-record(timeoutEvent, { ?EVENT, ?CYCLIC }).\n```\n\nGateways\n--------\n\nGateways represent multiplexors and demultiplexors which cause non-linear trace and multiple\ncurrent states as leaves of execution graph.\n\n```erlang\n-type gate()   :: exclusive | parallel | inclusive | complex | event.\n\n-record(gateway,      { ?TASK, type= parallel :: gate() }).\n```\n\nFull set of BPMN 2.0 fields could be obtained\nat [http://www.omg.org/spec/BPMN/2.0](http://www.omg.org/spec/BPMN/2.0), page 3-7.\n\nSample Session\n--------------\n\n```erlang\n(bpe@127.0.0.1)1\u003e rr(bpe).\n[beginEvent,container,endEvent,history,id_seq,iterator,\n messageEvent,process,sequenceFlow,serviceTask,task,userTask]\n(bpe@127.0.0.1)2\u003e bpe:start(spawnproc:def(),[]).\nbpe_proc:Process 39 spawned \u003c0.12399.0\u003e\n{ok,\u003c0.12399.0\u003e}\n(bpe@127.0.0.1)3\u003e bpe:complete(39).\n(bpe@127.0.0.1)4\u003e bpe:complete(39).\n(bpe@127.0.0.1)5\u003e bpe:complete(39).\n(bpe@127.0.0.1)5\u003e bpe:hist(39).\n[#history{id = 28,version = undefined,container = feed,\n          feed_id = {history,39},\n          prev = 27,next = undefined,feeds = [],guard = true,\n          etc = undefined,name = \"Order11\",\n          task = {task,\"end\"}},\n #history{id = 27,version = undefined,container = feed,\n          feed_id = {history,39},\n          prev = 26,next = 28,feeds = [],guard = true,etc = undefined,\n          name = \"Order11\",\n          task = {task,\"end2\"}},\n #history{id = 26,version = undefined,container = feed,\n          feed_id = {history,39},\n          prev = undefined,next = 27,feeds = [],guard = true,\n          etc = undefined,name = \"Order11\",\n          task = {task,\"begin\"}}]\n```\n\nProcess Instances\n-----------------\n\nInstantiation of process means creating persistent context of document flow.\n\n```erlang\nload(ProcName)\nstart(Proc,Docs)\namend(Proc,Docs)\ncomplete(Proc)\nhistory(ProcId)\ntask(Name,Proc)\ndoc(Name,Proc)\nevents(Proc)\ntasks(Proc)\n```\n\nUsing 'tasks' API you can fetch current documents attached to the given\nprocess at particular stage. Using 'amend' API you can upload or\nchange document at current stage. 'push' API moves current\nstage documents further by workflow.\n\nLet us see how we could create initial 'Wire Transfer' transaction:\n\n```erlang\n\u003e rr(bpe).\n[ beginEvent,boundaryEvent,container,endEvent,history,id_seq,\n  interval,iterator,kvs,log,messageEvent,operation,process,\n  receiveTask,sequenceFlow,serviceTask,task,timeoutEvent,userTask ]\n\n\u003e rr(kvs).\n[column,config,container,id_seq,interval,iterator,kvs,log,\n operation,query,schema,table,user,user2]\n\n\u003e Proc = bpe:load(39).\n\n\u003e bpe:tasks(Proc).\n  [#userTask{name = 'Init',roles = [], module = spawnproc},\n   #userTask{name = 'Signatory',roles = [], module = spawnproc},\n   #serviceTask{name = 'Payment',roles = [], module = spawnproc},\n   #serviceTask{name = 'Process',roles = [], module = spawnproc},\n   #endEvent{name = 'Final',module = []}]\n\n\u003e bpe:docs(Proc).\n  []\n\n\u003e bpe:amend(39,[{'WireTransfer',#user{id=1},#user{id=2}}]).\n\n\u003e bpe:docs(bpe:load(39)).\n```\n\nCredits\n-------\n\n* Maxim Sokhatsky\n* Oleksandr Naumov\n* Ivan Kulyk\n\nOM A HUM\n","funding_links":[],"categories":["Erlang","others"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsynrc%2Fbpe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsynrc%2Fbpe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsynrc%2Fbpe/lists"}