State

The workflow state keeps track of the state of the workflow as it progresses. The state is stored in the configured persistence, and can be used to map incoming messages to handlers.

Defining the state

A workflow state is created by extending WorkflowState. The $name property should be unique among all of your workflows.

import { WorkflowState } from '@node-ts/bus-core'

export class FulfilmentWorkflowState extends WorkflowState {
  static NAME = 'FulfilmentWorkflowState'
  $name = FulfilmentWorkflowState.NAME
  
  // Set of user-defined state properties
  itemId: string
  customerId: string
  status: 'posting-item' | 'sending-receipt' | 'complete'
}

Accessing the state

The state is available as the second parameter to all handlers of a workflow. For example:

import { Workflow } from '@node-ts/bus-core'

export class FulfilmentWorkflow extends Workflow<FulfilmentWorkflowState> {
  configureWorkflow (
    mapper: WorkflowMapper<FulfilmentWorkflowState, FulfilmentWorkflow>
  ): void {
    mapper
      .withState(FulfilmentWorkflowState)
      // ...
      .when(ItemShipped, 'emailReceipt')
  }
  
  // Workflow state is passed in as the second parameter to a workflow handler
  async emailReceipt (_: ItemShipped, state: FulfilmentWorkflowState) {
    // ...
  }
}

Updating the state

The state cannot be modified directly within a handling scope but can be updated by returning the intended changes from a handler. Returning an updated state is optional, and if omitted then no changes to the state will be persisted.

import { Workflow } from '@node-ts/bus-core'

export class FulfilmentWorkflow extends Workflow<FulfilmentWorkflowState> {
  configureWorkflow (
    mapper: WorkflowMapper<FulfilmentWorkflowState, FulfilmentWorkflow>
  ): void {
    mapper
      .withState(FulfilmentWorkflowState)
      // ...
      .when(ItemShipped, 'emailReceipt')
  }
  
  // Workflow state is passed in as the second parameter to a workflow handler
  async emailReceipt (_: ItemShipped, state: FulfilmentWorkflowState) {
    return { status: 'sending-receipt' }
  }
}

Discarding state

There are times when the workflow data shouldn't persist after a message has been handled. This is particularly relevant in cases where a workflow should only handle a message under certain circumstances.

For example, if your workflow is started by an S3ObjectCreated event, but should only create a new workflow if the object key is prefixed with /documents, then this can be achieved by returning this.discardWorkflow() in the workflow like so:

import { Workflow, BusInstance } from '@node-ts/bus-core'

export class ProcessDocumentWorkflow extends Workflow<ProcessDocumentWorkflowState> {
  constructor (bus: BusInstance) {}
  configureWorkflow (
    mapper: WorkflowMapper<ProcessDocumentWorkflowState, ProcessDocumentWorkflow>
  ): void {
    mapper
      .withState(ProcessDocumentWorkflowState)
      .startedBy(S3ObjectCreated, 'readDocument')
  }
  
  async readDocument (event: S3ObjectCreated) {
    if (event.s3Key.indexOf('/documents') === 0 {
      await this.bus.send(new ReadDocument(event.s3Key))
    } else {
      // Ignore this message and avoid persisting the workflow state
      return this.discardWorkflow()
    }
  }
}

Last updated