Redis Workflow
Dynamic rules engine to allow configurable workflow in app without requiring code changes.
Using Redis as backing service, you can design workflows and whenever you start
your workflow,
it will load your stored workflow(s) then attach a pubsub
listener to Redis. Any time your
pubsub
channel message appears, it will parse the event object, perform conditional logic, and if true
emit one or more defined actions.
This app is loosely-based on popular enterprise systems workflow rules or business process features.
- Trigger (or event) - something happened
- Conditions (or filters) - criteria to take action
- Actions (or tasks) - what to do
Requirements
You must have Redis server running. This app treats redis like a persistent backup. It uses pubsub
as a listener
for trigger events. It uses it to save and load workflows to avoid loss. The set
and get
workflows methods
just interact with memory and not database. See the IWorkflowManager
interface source for more.
Extensibility
Most non-public properties and methods are protected
so you may sub-class and override. RedisWorkflowManager
is my
preferred implementation but you could also write a new manager class that implements IWorkflowManager
interface.
Installation
npm install redis-workflowyarn add redis-workflow
Test
The test script in package.json
preprocesses the .ts
file and then executes.
npm test
(also can run npm run coverage
)
Usage
The source was written in Typescript, yet it compiles to Javascript (npm run build
). You can use in ES5 or later supported environments. The following code snippets are implemented in the __tests__
folder.
Quick start (Node)
const redis = ;const flow = ; // instantiateconst config = flow;const client = redis; // optionally pass into manager 2nd param to shareconst manager = config; // create first workflowconst trigger = "myTrigger";const rule = "myRule" "foo == bar";const action = "myAction";const workflow = "myWorkflow" trigger rule action; // add workflow to managermanager; // add listener for actionmanager; // add error handlermanager // start workflow enginemanagerstart"myChannel"; // publish a test object to the pubsub channel; // sometime later ...; // note, if you publish an event and conditions do not == true, no action taken
Typescript
See more detailed examples in the respective src/__tests__
and src/lib/__tests__
folders.
Example with multiple rules and actions for a workflow
;; // optionally import each class (see __tests__) ;;; // just for our example to pubsub message // build test workflow;;;; // context added later when triggered;; // add first workflow to managermanager.setWorkflows; // errorsmanager.onWorkflowEvents.Error, // delayed actionsmanager.onWorkflowEvents.Schedule,; // immediate actionsmanager.onWorkflowEvents.Immediate,; // optionally handle actions by namemanager.on"adjustInventory",; // optionally handle global actions (all types)manager.onWorkflowEvents.Audit,; // optionally handle actions that didn't meet rulesmanager.onWorkflowEvents.Invalid,; // start manager (subscribes to pubsub channel)manager.start"babyDivision" .then; // build and publish trigger events to Redis pubsub channel; // simulate time after manager starts before triggers appearsetTimeout, 3000;
Triggers
Uses Redis pubsub
listening for events to start workflow.
;
Payload
The message
published to the topic will include a stringified JSON object as follows.
"event": "test" // must equal Trigger name "context": "myField": "myValue" "anotherField": "anotherValue"
Conditions
Uses mozjexl
Javascript expression language to evaluate string expressions, evaluating to true
or false
.
;
Actions
You define actions when building workflows. The action name will become an EventEmitter
event you handle.
// optionally namedmanager.on"eventName",; manager.onflow.WorkflowEvents.Schedule,; manager.onflow.WorkflowEvents.Immediate,;
For each Action
you add to your workflow, you one or more listeners like above. You can decide what functionality
your application performs if conditions are met, and actions are emitted.
Suggested action types
- Create record(s)
- Update record(s)
- Trigger another workflow
- Send message(s) or notification(s)
Scaling
This implementation using pubsub
can scale by leveraging different channels per instance, with a fanout. If you
want workers, I would override the start
method to use Redis blrpoplpush
(blocking pop of list) and publish
trigger events to it instead. This way you can spin up unlimited workers and once one pops from list, others ignore.
Resilience
If you instantiate the manager and pass in the third optional argument channels: string[]
, the app will attempt
to load workflows from the database.
Contributing
I haven't thought that far ahead yet. I needed this for my project and wanted to give back. ;-)
License
MIT (if you enhance it, fork and PR so the community benefits)