serverless-workflow-manager

Serverless Workflow Manager

A lightweight yet powerful, event driven workflow orchestration manager for micro-services. The workflow manager is built using hexagonal architecture).

It is fault tolerant, horizontally scalable & deployable as microservice. It has already powered more than a million workflows and has been open sourced under MIT License.

This library was successfully used at FundsCorner to power a number of business workflows (Online seller loans, Supply Chain Financing and Car Loans).

CircleCI CodeFactor

Key Features

  1. Ability to declaratively author business workflows for distributed micro-services, including Human in the loop approvals.
  2. Enables modeling of any complex inter-service dependency with ease, with an additional benefit of providing versioning and audit trail.
  3. All the events processed are persisted in a separate collection, making the entire workflow observable and auditable.
  4. Ability to replay events for a specific stage or task multiple times when required. I.e. the workflow can be resumed at any point in time.

The workflow manager is built based on Onion Architecture. The Message Queue handle and Database Repositories are injected into workflow manager, thus making the workflow manager persistence ignorant and infrastructure ignorant

Source Code

GitHub Repository

Underlying Concepts

Workflow State Machine

The workflow manager uses a workflow state machine template to acquire an orchestration plan. Here are the business rules for the workflow:

Consider the example of a pizza delivery workflow. The different stages and tasks involved can be pictorially depicted as follows: Food delivery workflow

The workflow template for this image can be represented as a json as follows:

{  
    "workflow_name" : "Deliver Pizza", 
    "component_name" : "ITALIAN", 
    "stages" : [
        {
            "stage_name" : "START", 
            "stage_order" : 0.0
        }, 
        {
            "stage_name" : "ORDER", 
            "stage_order" : 1.0, 
            "tasks" : [
                {
                    "task_name" : "confirm_order", 
                    "parent_task" : [], 
                    "task_queue" : "confirm_order_queue", 
                    "task_type" : "SERVICE", 
                    "business_status" : "ORDER CONFIRMED"
                }
            ]
        }, 
        {
            "stage_name" : "PREPARE", 
            "stage_order" : 2.0, 
            "tasks" : [
                {
                    "task_name" : "make_food", 
                    "parent_task" : [], 
                    "task_type" : "SERVICE", 
                    "task_queue" : "make_food_queue"
                }, 
                {
                    "task_name" : "assign_executive", 
                    "parent_task" : [], 
                    "task_queue" : "assign_executive_queue", 
                    "task_type" : "SERVICE"
                }, 
                {
                    "task_name" : "confirm_delivery", 
                    "parent_task" : [
                        "make_food", 
                        "assign_executive"
                    ], 
                    "task_queue" : "confirm_delivery_queue", 
                    "task_type" : "SERVICE", 
                    "business_status" : "FOOD ON THE WAY"
                }
            ]
        }, 
        {
            "stage_name" : "DELIVER", 
            "stage_order" : 3.0, 
            "tasks" : [
                {
                    "task_name" : "deliver_food", 
                    "parent_task" : [], 
                    "task_type" : "HUMAN", 
                    "task_queue" : "deliver_food_queue", 
                    "assigned_to" : "delivery_executive", 
                    "business_status" : "FOOD DELIVERED"
                }
            ]
        }
    ]
}

Workflow Persistence

The workflow manager persists and tracks the current state of the workflow (i.e. the workflow instance) in a separate database collection.

Workflow events

The workflow manager listens to a specific queue onto which other components communicate through events, as follows.

Event Name Description
StartWorkflow This event is sent by an API Service to start a workflow. Upon receiving this event, the workflow manager creates a workflow instance and schedules the tasks
TaskCompleted This event is sent by a micro - service task to signal that the scheduled work has been completed. Upon receiving this event, the workflow manager looks for next set of tasks to schedule, if any. The workflow manager also advances the stages, if required. If all the tasks under all the stages are completed, the workflow is considered closed.
TaskFailed This event is sent by a micro - service task to signal that the task has failed. Upon receiving this event, the workflow manager just persists this status to the workflow instance. A separate re-trier component may be implemented to functionally handle the retries of this task.
TaskPending This event is sent by a re-trier task to reschedule a task. Upon receiving this event, thw workflow manager reschedules this task.

Food delivery workflow

Workflow event samples

StartWorkflow

{
    "business_ref_no" : "ORDER-001", 
    "component_name" : "ITALIAN", 
    "event_name": "StartWorkflow"
}

Payload from Workflow when a Task is scheduled

{
    "business_ref_no": "ORDER-001",
    "component_name": "ITALIAN",
    "event_name": "StartTask",
    "stage_name": "ORDER",
    "task_name": "confirm_order",
    "task_type": "SERVICE"
}

If a task contains additional fields configured in the workflow template, the workflow manager will pack them in this payload.

TaskCompleted signal from Task

{
    "business_ref_no": "ORDER-001",
    "component_name": "ITALIAN",
    "event_name": "TaskCompleted",
    "stage_name": "ORDER",
    "task_name": "confirm_order",
    "task_type": "SERVICE"
}

TaskFailed signal from Task

{
    "business_ref_no": "ORDER-001",
    "component_name": "ITALIAN",
    "event_name": "TaskPending",
    "stage_name": "ORDER",
    "task_name": "confirm_order",
    "task_type": "SERVICE"
}

TaskPending signal from Retry manager

{
    "business_ref_no": "ORDER-001",
    "component_name": "ITALIAN",
    "event_name": "TaskPending",
    "stage_name": "ORDER",
    "task_name": "confirm_order",
    "task_type": "SERVICE"
}