Skip to main content

Quickstart

Implementing Order Processing with NestJS Workflow

This guide will walk you through setting up the NestJS Workflow module to manage the state transitions of an order processing system.

Prerequisites

  • A NestJS application set up with TypeScript.
  • Node.js and npm installed.

Step 1: Install the NestJS Workflow Module

Install the module using npm:

npm install @jescrich/nestjs-workflow

Or with yarn:

yarn add @jescrich/nestjs-workflow

Step 2: Define the Order Entity and Enums

Create the Order entity along with enums for order events and statuses:

// order.model.ts

export enum OrderEvent {
Create = 'order.create',
Submit = 'order.submit',
Update = 'order.update',
Complete = 'order.complete',
Fail = 'order.fail',
Cancel = 'order.cancel',
}

export enum OrderStatus {
Pending = 'pending',
Processing = 'processing',
Completed = 'completed',
Failed = 'failed',
}

export class Order {
urn: string;
name: string;
price: number;
items: string[];
status: OrderStatus;
}

Step 3: Define the Workflow

Create a workflow definition that outlines the states and transitions for the order:

// order.workflow.ts

import { WorkflowDefinition } from '@jescrich/nestjs-workflow';
import { Order, OrderEvent, OrderStatus } from './order.model';

export const orderWorkflowDefinition: WorkflowDefinition<Order, any, OrderEvent, OrderStatus> = {
FinalStates: [OrderStatus.Completed, OrderStatus.Failed],
IdleStates: [OrderStatus.Pending, OrderStatus.Processing, OrderStatus.Completed, OrderStatus.Failed],
Transitions: [
{
from: OrderStatus.Pending,
to: OrderStatus.Processing,
event: OrderEvent.Submit,
conditions: [(entity: Order) => entity.price > 10],
},
{
from: OrderStatus.Pending,
to: OrderStatus.Pending,
event: OrderEvent.Update,
actions: [
(entity: Order, payload: any) => {
entity.price = payload.price;
entity.items = payload.items;
return Promise.resolve(entity);
},
],
},
{
from: OrderStatus.Processing,
to: OrderStatus.Completed,
event: OrderEvent.Complete,
},
{
from: OrderStatus.Processing,
to: OrderStatus.Failed,
event: OrderEvent.Fail,
},
],
FailedState: OrderStatus.Failed,
Entity: {
new: () => new Order(),
update: async (entity: Order, status: OrderStatus) => {
entity.status = status;
return entity;
},
load: async (urn: string) => {
// Implement logic to load the order entity by URN
return new Order(); // Placeholder implementation
},
status: (entity: Order) => entity.status,
urn: (entity: Order) => entity.urn,
},
};

Step 4: Register the Workflow Module

Import and register the WorkflowModule in your NestJS application module:

// app.module.ts

import { Module } from '@nestjs/common';
import { WorkflowModule } from '@jescrich/nestjs-workflow';
import { orderWorkflowDefinition } from './order.workflow';

@Module({
imports: [
WorkflowModule.register({
name: 'orderWorkflow',
definition: orderWorkflowDefinition,
}),
WorkflowModule.forRoot({
storage: {
type: 'memory', // For production, consider using 'database'
},
}),
],
})
export class AppModule {}

Step 5: Utilize the Workflow in a Service

Inject the workflow into your service and define methods to handle order events:

// order.service.ts

import { Injectable, Inject } from '@nestjs/common';
import { Workflow } from '@jescrich/nestjs-workflow';
import { Order, OrderEvent } from './order.model';

@Injectable()
export class OrderService {
constructor(
@Inject('orderWorkflow')
private readonly orderWorkflow: Workflow<Order, OrderEvent>,
) {}

async submitOrder(urn: string) {
return await this.orderWorkflow.emit({ urn, event: OrderEvent.Submit });
}

async updateOrder(urn: string, price: number, items: string[]) {
return await this.orderWorkflow.emit({
urn,
event: OrderEvent.Update,
payload: { price, items },
});
}
}

Step 6: Implement Class-Based Actions with Decorators

For more complex workflows, you can define actions using decorators:

// order.actions.ts

import { Injectable } from '@nestjs/common';
import { WorkflowAction, OnEvent, OnStatusChanged } from '@jescrich/nestjs-workflow';
import { Order, OrderEvent, OrderStatus } from './order.model';

@Injectable()
@WorkflowAction()
export class OrderActions {
@OnEvent({ event: OrderEvent.Submit })
execute({ entity, payload }: { entity: Order; payload: any }) {
entity.price *= 100;
return Promise.resolve(entity);
}

@OnStatusChanged({ from: OrderStatus.Pending, to: OrderStatus.Processing })
onProcessing({ entity }: { entity: Order }) {
entity.name = 'Processing Order';
return Promise.resolve(entity);
}
}

Ensure to register these action classes as providers in your module:

// app.module.ts

import { Module } from '@nestjs/common';
import { WorkflowModule } from '@jescrich/nestjs-workflow';
import { orderWorkflowDefinition } from './order.workflow';
import { OrderActions } from './order.actions';

@Module({
imports: [
WorkflowModule.register({
name: 'orderWorkflow',
definition: orderWorkflowDefinition,
}),
WorkflowModule.forRoot({
storage: {
type: 'memory',
},
}),
],
providers: [OrderActions],
})
export class AppModule {}

Conclusion

By following these steps, you've set up the NestJS Workflow module to manage the state transitions of an order processing system. This structured approach enhances code maintainability and aligns with best practices for enterprise application development.