Skip to content

Instantly share code, notes, and snippets.

@aleung
Last active March 15, 2019 10:40
Show Gist options
  • Save aleung/68e7b2f64a6e8a235d04b07c9d81dfe6 to your computer and use it in GitHub Desktop.
Save aleung/68e7b2f64a6e8a235d04b07c9d81dfe6 to your computer and use it in GitHub Desktop.
TypeScript logic flow execution engine

Async operation procedure execution engine

  • Reusable, recoverable operations
  • Reusable procedures
  • High-level orchestration API

Benefit

  • Break down business logic complexity
  • Rapid development
  • Easy customization to adapt customer requirement
  • Built-in command pattern to simplify operation rollback design
async function operationOne(context: SharedContext, input: Readonly<Op1Input>): Promise<[Op1Output, RollbackFn]> {
const id = input.id;
await saveDb(id, true);
const result = 'OK';
const rollback = async (context: SharedContext) => {
await saveDb(id, false);
}
return [result, rollback];
}
async function myProc(flow: Flow, input) {
const x = await flow.runOp(operationOne, input.id);
const output = await flow.runOp(operationTwo, x);
return output;
}
const context = { ... };
new Flow(context).run(myProc, {id: 123});
interface SharedContext {
[propName: string]: any;
}
type RollbackFn = (context: SharedContext) => Promise<void>;
type Operation<Input, Output> = (context: SharedContext,
input: Readonly<Input>) => Promise<[Output, RollbackFn | undefined]>;
type Procedure<Input, Output> = (flow: Flow, input: Readonly<Input>) => Promise<Output>;
class Flow {
private readonly rollbackStack: RollbackFn[] = [];
constructor(public context: SharedContext) {
}
// call an operation
async runOp<Input, Output>(operation: Operation<Input, Output>, input: Readonly<Input>): Promise<Output> {
try {
const [output, rollback] = await operation(this.context, input);
if (rollback) {
this.rollbackStack.push(rollback);
}
return output;
} catch (err) {
throw err; // RollbackError
}
}
async runProc<Input, Output>(proc: Procedure<Input, Output>, input: Readonly<Input>): Promise<Output> {
try {
return await proc(this, input);
} catch (err) {
await this.rollback();
throw err;
}
}
private async rollback(): Promise<void> {
while (this.rollbackStack.length > 0) {
const func = this.rollbackStack.pop();
await func!(this.context);
}
}
}
export { SharedContext, RollbackFn, Flow };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment