Overview

Cloud CMS provides an enterprise workflow engine that lets teams work together via coordinated tasks. A workflow is a customizable business process that you define and which spells out a series of steps and activities to be performed by your team members. Workflows are generally used to control the lifecycle state (approval and rejection) of one or more content items or documents.

With workflow in place, documents are routed from user to user. Users optionally receive notifications via email and see tasks assigned to them in their task inbox. Users work on tasks, make modifications to documents and document metadata, assign tasks them to other team member and transition tasks by approving or rejecting them.

The Cloud CMS workflow engine is a high powered subsystem that was built from the ground up on top of Amazon AWS and MongoDB. It was inspired by traditional enterprise BPM engines that the engineers were familiar with, such as JBoss JBPM5, Alfresco's Activiti BPM and Adobe/Day Software's CQ5. It improves on these by simplifying things and integrating naturally to Cloud CMS via a JSON-oriented approach.

Why workflow?

Some folks who are not familiar with the concept of workflow might ask why it is necessary.

Typically, workflow is used whenever you want to capture some kind of process of sequence of activities that must be performed or signed off on before a document is approved.

For example, you might need to approve a series of articles that you're working on for your web site. If you set up a workflow, you can specify the sequence of activities that need to be performed, including authoring, review and final approval. These tasks might be performed by different people on different teams. A workflow process gives you a way to collect the sequence of steps as well as the history of each activity along the way so that anyone working on the article can see what happened, review comments and make updates.

Workflows are role-based which means that you can not only sequence the tasks but you can also finely describe who can work on each task and what role they have. Roles give you a way to limit or expand functionality depending on what capabilities you intend each participant to have along the way.

Core Concepts

In explaining workflow, let's first take a look at the following things:

  • Workflow Models
  • Workflow Instances
  • Workflow Tasks
  • Workflow Payload Resources
  • Workflow Comments
  • Workflow History Item
  • Workflow Events
  • Workflow Event Handlers

A workflow model is a definition of a workflow consisting of a full set of instructions on how content should be routed between participants or activities on its way toward completion. A workflow model is kind of like a blueprint describing the sequence of steps to be taken.

A workflow instance is a running workflow that has been started. Each instance runs a workflow model's instructions from start to end. A workflow instance maintains properties (data), attached payload resources (content) and other lifecycle state.

A workflow payload resource is a piece of content (a content node) that has been attached to a workflow instance. Every workflow instance can have zero or more payload resources. You can also start workflows that don't have any payload resources if you want to.

A workflow task is an assignment that is created when a workflow transitions from one state to the next. The task holds point-in-time information about an activity to be completed. Each task holds its own copy of the workflow's properties (data) and attached payload resources (content) so that it can be worked on in isolation. When a task completes, the data and content are merged back into the workflow instance. And then the workflow continues on.

A workflow comment can be created at any time and is attached to both a workflow task and a workflow instance. This lets you see comments made for a specific task or comments made across the entire workflow.

A workflow history item is an automatically managed object that gets created in the background every time someone interacts with a workflow. It supplies a record of every change to the workflow task and workflow instance, letting you see a perfect history of when people modified properties (data), updated payload resources (content), added comments, fired actions or transitioned between workflow tasks.

A workflow event is a lifecycle event that is raised as the workflow starts, ends or moves from task to task. There are a number of potential events that are raised and each workflow model has the opportunity to render one or more workflow handlers to optionally handle these events.

A workflow event handler is an event listener that hears when a workflow event is raised and then does something. These are a number of workflow event handlers available out-of-the-box including handlers for web hook callouts (over HTTP), email and Cloud CMS actions. You configure workflow event handlers with a little bit of JSON.

Workflow Models

A workflow model is a JSON document that defines a workflow. In a sense, a workflow model is a full set of instructions about how a process should be routed on its way toward completion. A workflow model consists of the following:

  • Nodes
  • Transitions
  • Swimlanes
  • Forms

Nodes and Transitions

A workflow model is foremost defined by a graph that contains a start node and an end node. The graph defines a flow that the process takes. The flow starts at the start node and ultimately must end up at the end node. You can set up any structure you'd like between the start and end, including conditional flow paths, parallel and serial flow paths and forks and joins.

Let's take a look at a really simple example:

{
    "title": "Approve an article #1",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "initiator"
        }
        "end": {
            "type": "end"
        }
    }
}

This example defines a one-hop approval process for an article.

This example workflow has three nodes (start, node1 and end). Every workflow has a start node and an end node. The workflow defines a transition from the start node to the node1 node named start. This transition gets fired automatically when the workflow starts. Technically, it can be named anything. The start node is a special case in that it is only allowed one transition.

It also defines a transition from the node1 node to the end node named approve. The node1 node is of type participant. Nodes can have different types. In this case, the participant type means that the workflow should wait here for the end user to decide what to do. From a user interface viewpoint, this usually appears as a button that the user will click to advance the workflow.

When the end user clicks on approve, the workflow transitions to the end state and the workflow completes.

The swimlane attribute is used to identify the actor (or end user) who is allowed to interact with the node. In this case, the implicit initiator swimlane is used. This is an automatic swimlane which identifies the user who launched the workflow. Thus, in this case, the initiator who started the workflow essentially ends up with an approval process where they themselves do the approval. Not all that useful, in the end, but what the heck, it's just a sample. For more examples of swimlanes, read on.

Swimlanes

A swimlane is used to define an actor who is participating in the workflow. In general, you will need to have one swimlane per participant in the workflow. A swimlane gets its colorful name from a lane within a swimming pool where only one person is allowed to swim in the lane at a time.

When a workflow node is of type participant, it will need to identify a swimlane that tells it which actor is participating. By default, every workflow automatically has an implicit initiator swimlane which contains the user who initiated the workflow.

Let's expand our example workflow to include a custom swimlane:

{
    "title": "Approve an article #2",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    }
}

The swimlanes configuration block identifies the swimlanes by ID and provides for each an array of potential principals that can be actors within the swimlane. If the swimlane is empty (i.e. []) as shown above, then any principal could fill the role. But none are selected by default.

If you try to start a workflow such as the one above, it will fail to start. You'll get an error back stating that one or more of the swimlanes needs to be assigned before the workflow can be started. Indeed, before a workflow can be started, all of its swimlanes must be assigned.

Note that once a workflow is started, it is possible to delegate authority to work on a node to another principal. It is also possible as another principal to claim or unclaim authority. In either case, the other principal must be identified within the swimlane actors array (or the array must be empty, meaning anyone can be assigned).

Here is another example where we've identified two users who fulfill the manager actor role:

{
    "title": "Approve an article #3",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": [
                "primary/pvenkman",
                "primary/estengler"
            ]
        }
    }
}

As before, in order to start this workflow, we first need to tell the workflow engine which actor will take on the manager swimlane. Let's say that we pick primary/pvenkman. Now the workflow begins and Peter Venkman is assigned the node1 approval task. He goes to his task screen and sees a task waiting for him to work on.

At this this point, Peter might decide he needs to pass the task on to someone else. So he decides to delegate the task. In doing so, he will be given a list of potential delegates and only Egon Stengler (primary/estengler) will appear in the list. Similary, Peter may elect to unclaim the task. Unclaiming the task will release it to the swimlane meaning that no one is working on it. However, both Peter Venkman and Egon Stengler will see the task available to them as something they could start working on, if they wanted to. One of them simply needs to claim it.

While it is typical for swimlanes to map to participant nodes, it does not necessarily imply that you will have one swimlane per node. In fact, swimlanes may often be used across many nodes in cases where a particular user who worked on something earler may need to work on something again later. Swimlanes give your business process a means for retaining information about who was involved in the process and involving them against at a later point.

Forms

A workflow maintains a JSON payload (or property set) that is modified during the flow and kept after the workflow completes. As the workflow moves from one node to the next, it copies this JSON payload down into workflow tasks so that it can be edited by separate users individually. When the workflow task is completed, the data is merged back into the parent workflow instance. This allows many users to work on things at once, with each task worked upon in isolation.

Each workflow has a set of standard properties and is fully customizable with your own properties. You can define the schema as well as the layout options for forms utilized by the workflow.

The following properties are optional but always supported:

  • title
  • description
  • instructions
  • dueDate
  • priority

And you're free to add your own as you see fit.

Let's enhance our example workflow to include some custom form properties:

{
    "title": "Approve an article #4",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": [
                "primary/pvenkman",
                "primary/estengler"
            ]
        }
    },
    "forms": {
        "global": {
            "schema": {
                "type": "object",
                "properties": {
                    "topic": {
                        "type": "string",
                        "title": "Topic"
                    }
                }
            },
            "options": {
                "fields": {
                    "topic": {
                        "type": "textarea"
                    }
                }
            }
        }
    }
}

Properties are defined using JSON Schema. Cloud CMS natively works with JSON schema to server-side validate your data and produce user-interface forms.

Above, we define a global form which is the top-level work that is globally scoped to the entire workflow model. Every task will receive this form as a definition of any data bindings. The schema sub-property provides the JSON schema of the data. It is required. The options sub-property is optional and provides form configuration to drive the underlying Cloud CMS forms engine. For details on the kinds of customizations possible, check out Alpaca Forms.

The form shown above is scoped to the workflow instance itself. If you wanted to define some properties at the workflow instance level and other properties at the node level, you're free to do so. Just bear in mind that node level properties inherit from workflow instance properties. You cannot define the same properties at both levels.

Here is an example that includes node-level properties:

{
    "title": "Approve an article #5",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "form": "node1form"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": [
                "primary/pvenkman",
                "primary/estengler"
            ]
        }
    },
    "forms": {
        "global": {
            "schema": {
                "type": "object",
                "properties": {
                    "topic": {
                        "type": "string",
                        "title": "Topic"
                    }
                }
            },
            "options": {
                "fields": {
                    "topic": {
                        "type": "textarea"
                    }
                }
            }
        },
        "node1form": {
            "schema": {
                "type": "object",
                "properties": {
                    "stars": {
                        "type": "number",
                        "title": "Rating"
                    }
                }
            }
        }
    }
}

If during the course of this workflow the end user works with forms generated from the properties as per the example above, the resulting workflow JSON might include the following:

{
    "topic": "Agriculture",
    "stars": 3
}

Workflow Models and Deployment

Every workflow model has a model ID which uniquely identifies the workflow model. It can be any string you'd like though convention is to use a QName (such as custom:workflow1). When you create a workflow model, it is automatically tagged with a version number.

A workflow model can be modified until you're happy with it. To use it, it must then be deployed. Once a workflow model is deployed, that version of the workflow model is frozen. It cannot be modified any longer under the assumption that there may be in-flight workflow instances using this specific version of the model. Those in-flight workflow instances require the workflow model to be available to provide routing instructions.

You can delete a deployed workflow model only if there are zero in-flight workflow instances.

To make changes to a deployed workflow model, you must first create a new version of the workflow model. The new version of the workflow model has the same ID but a different version. Any current in-flight workflows continue to use the old workflow model. Even after you deploy the new version of your model, any pre-existing in-flight workflows will continue to use the old model where as new workflow instances will use the new model.

Workflow Node Types

Each workflow node defined in a workflow model must provided with a type attribute that identifies the type of workflow node being configured. The following workflow node types are supported:

Type Description
START The start node of a workflow. There should always be one START node. The START node must have at least one outgoing transition.
END The end node of a workflow. There should always be one END node. The END node must not have any outgoing transitions.
PARTICIPANT A node that requires end user interaction. A swimlane must be assigned. You can have as many PARTICIPANT nodes as you would like. A PARTICIPANT node may have as many outgoing transitions as you would like.
PASSTHRU A node that simply passes through. A PASSTHRU node must have exactly one outgoing transition.

Workflow Instances

A workflow instance is an in-flight process that is being managed by Cloud CMS. It is a live process that is actively running and looking to a workflow model as a blueprint for what to do next. Each workflow instance retains a reference to a specific version of a workflow model. As future models are deployed, they are incrementally versioned so that pre-existing workflow instances continue to use the older workflow model until they finish.

Workflow Tasks

As a workflow instance is routed through the BPM engine, individual workflow tasks are created for your workflow model's nodes. A task is a unit of work to be accomplished. For non-participant nodes, a workflow task is automatic and simply processes in the background. For PARTICIPANT nodes, a workflow task is created and stays alive until an end user interacts with the workflow task and tells it what to do.

Each workflow task has its own copy of the payload resources (nodes) and the process data (properties). When an end user makes changes to the documents or properties, they do not affect any other users working on other tasks. In this way, tasks can be parallelized so that multiple groups can work on multiple tasks at the same time.

When a task is completed, the payload resources and process data is merged back into the workflow instance.

Workflow Payload Resources

Each workflow instance can have zero or more payload resources. These are usually content nodes (such as a PDF or a Word document that you attach for approval). Some workflow instances may not have any payload resources. These are content-less flows and are used for doing things like routing requests or signaling change updates.

Payload resources are copied into workflow tasks upon creation, enabling multiple users to work together at the same time without stepping over each other. When tasks are completed, the task instances are archived and a history snapshot is computed showing differences accumuluated within each task of the workflow over time (changes to workflow data, workflow resources, comments and more).

Workflow Comments

Each workflow can have zero or more comments attached to it. Comments are attached to workflow instances while maintaining a reference to the workflow task in which they were created. Comments are also ordered and timestamped in addition to supporting full query, sorting and pagination on retrieval.

When comments are made, a workflow history record is created automatically so that an accurate historical view of modifications to the workflow is kept. This includes the addition or modification of comments.

Workflow History

As tasks are completed, no matter whether automatic or PARTICIPANT in nature, workflow history records are created and stored in the background. In this way, every change to the data properties, payload documents, comments or other elements of the workflow are logged.

When a workflow instance completes, it is possible then to consult the workflow history records to see a full set of changes from the workflow instance's beginning through to it's end. The workflow history records comprise the full set of adjustments that can be applied to the workflow instance at the beginning to reproduce the workflow instance at the end, should you desire to do so.

Each workflow history record also contains information about the start time, end time and the user who made the change. In coordination with the Cloud CMS auditing service (which logs data access at the service and object level), the workflow history mechanism provides chronological and activity data that lets you audit your business processes to see who worked on what, made what changes and at what time.

Along with other aspects of the workflow service within Cloud CMS (and frankly, along with pretty much everything else in Cloud CMS), you are free to query, read, update, report and work with workflow history records as you see fit.

Workflow Events

Workflow events are raised as the workflow "flows" through its lifecycle. By default, there are no registered handlers and so these go unnoticed. But, they provide a place for you to hook in custom business logic through the registration of one or more workflow handlers per event type.

Workflow events are grouped into the following kinds:

  • workflow
  • task
  • transition

The following workflow events are raised during the lifecycle of a typical workflow:

Event ID Kind Description
START workflow Raised when a workflow starts
END workflow Raised when a workflow ends
ENTER task Raised when a workflow begins a task
LEAVE task Raised when a workflow finishes a task
TRANSITION transition Raised when a workflow is about to transition from a source task to a target task

Workflow Event Handlers

Workflow event handlers are registered within your workflow model at either the instance-scope or the task-scope. As with other elements in your model, the task-scope inherits from the instance-scope which means that you are allowed to globally declare task events (like ENTER or LEAVE) at the instance level if you wish to. Doing so will apply the same task event handlers to all tasks.

Using our really simple example, let's add logger event handlers that fire when the workflow starts (START) and when the first task completes (LEAVE).

{
    "title": "Approve an article #1",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "initiator",
            "handlers": {
                "LEAVE": [{
                    "type": "logger",
                    "config": {}
                }]
            }
        }
        "end": {
            "type": "end"
        }
    },
    "handlers": {
        "START": [{
            "type": "logger",
            "config": {}
        }]
    }
}

The handlers blocks in the sample above provide two pieces of information that Cloud CMS needs to know in order to fire your handler. The first is the type and the second is the optional config. Some handlers require config and others do not.

There are several out-of-the-box handlers available:

Workflow Configuration

When instantiating workflows programatically, the following optional runtime properties will be inherited by any event handlers that rely upon these values:

  • runtime.applicationId
  • runtime.emailProviderId
  • runtime.repositoryId
  • runtime.branchId

Sending Emails

A common scenario is to have a workflow send emails to participants as it transitions from node to node within the workflow graph. There are multiple ways to do this. One way, as you've seen, is to use an Email Handler to take full control of how emails are sent. Another option is to take advantage of configuration-driven emails using straight up JSON configuration.

Simple Emails

The easiest way to get started is to simply set email: true on the node definition for any participant node within the workflow model. When set to true, a stock email will be sent to the participant to let them know that they have been assigned a task. The stock email provides a link back into Cloud CMS so that the email recipient can click on it to get right to work.

Here is an example of a simple workflow with stock emails turned on:

{
    "title": "Simple Email Example",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "email": true
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    }
}

Configured Emails

You can also provide a JSON object that defines the email that is to be sent. The JSON object can provide the following properties:

{
    "to": "<the recipient email address>",
    "body": "<the body of the email>",
    "subject": "<the subject of the email>",
    "cc": "<the CC address for the email>",
    "bcc": "<the BCC address for the email>"
}

If the to field is not specified, the participant's email address will be filled in automatically.

Here is an example of a simple workflow with configured emails turned on:

{
    "title": "Configured Email Example",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "email": {
                "subject": "Ahoy Lad! A task awaits ye!",
                "body": "Please <a href='${link}'>take a look at this task</a>"
            }
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    }
}

Configured Emails (Reuse)

You can also store your email configurations globally and reference them from within the node definition so as to reuse them across various stages of your workflow.

Here is the same example with a named configuration for reuse.

{
    "title": "Configured Email Example",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "email": "notificationEmail"
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    },
    "emails": {
        "notificationEmail": {
            "subject": "Ahoy Lad! A task awaits ye!",
            "body": "Please <a href='${link}'>take a look at this task</a>"
        }
    }
}

Email Templates

You might want to store your email templates within Cloud CMS and reuse them within your workflow. That way, you can change your emails at any time without having to make adjustments to your workflow model.

You can do this by specifying a templateKey. The template key matches up to n:email-template instances by looking them up either by _doc or by key.

Here is an example:

{
    "title": "Email Template Example",
    "nodes": {
        "start": {
            "type": "start",
            "transitions": {
                "start": "node1"
            }
        },
        "node1": {
            "type": "participant",
            "title": "Approve the article",
            "transitions": {
                "approve": "end"
            },
            "swimlane": "manager",
            "email": {
                "templateKey": "myEmailTemplateKey",
                "subject": "Hello World!"
            }
        }
        "end": {
            "type": "end"
        }
    },
    "swimlanes": {
        "manager": {
            "principals": []
        }
    }
}

This will look for an n:email-template instance with key equal to myEmailTemplateKey.

Email Template Variables

The fields of your email (such as subject or body) can take advantage of the workflow model made available to you. The following variables are available to you depending on what type of workflow lifecycle event is being handled:

For workflow emails, the following variables are available:

  • workflow - () The workflow instance

For task emails, the following variables are available:

  • workflow - () The workflow instance
  • workflowTask - () The current workflow task
  • initiator - the principal who initiated the workflow
  • assignee - if the workflow task is assigned, the principal (either a user or group) that is the assignee
  • delegates - a list of principals who serve as delegates (possible recipients for the pooled task)
  • previousWorkflowTask - the prior workflow task (if applicable)
  • previousAssignee - the principal who was assigned to the prior workflow task (if applicable)

For transition emails, the following variables are available:

  • workflow - () The workflow instance
  • initiator - the principal who initiated the workflow
  • transition - () The workflow transition
  • sourceWorkflowTask - () The source workflow task
  • sourceAssignee - if the source workflow task is assigned, the principal (either a user or group) that is the assignee
  • sourceDelegates - a list of principals who serve as delegates for the source workflow task
  • targetWorkflowTask - () The target workflow task
  • targetAssignee - if the target workflow task is assigned, the principal (either a user or group) that is the assignee
  • targetDelegates - a list of principals who serve as delegates for the target workflow task
  • previousWorkflowTask - the prior workflow task (if applicable)
  • previousAssignee - the principal who was assigned to the prior workflow task (if applicable)