Orchestration with Node.js



Resources
Principle

Orchestration is the way of modeling and implementing how service consumptions when chained, forked… map to business processes. Web Services Business Process Execution Language -WS-BPEL- is a candidate tool for orchestration of Web services. Web service requests are chained and/or forked so that orchestration leads to a meaningful flow with regard to the business logic.

While Web Services Business Process Execution Language -WS-BPEL- is concerned with external orchestration, differently, server-side components may play an equivalent role in an internal way. Service requests are forwarded to such components that finalize (i.e., process) or ignore these requests according to the business logic and a well-established realtime context.

Orchestration based on X-STATE

Stateful components keep states accross requests. Design of complex behaviors as orchestration (coordination, negotiation for instance) benefits from libraries like X-STATE, which complies with the W3C State Chart XML -SCXML- standard.

Requests are viewed as events on transitions, that are (or not) process depending upon states defining the current context. For example, a Fire Station Coordinator -FSC- and a Police Station Coordinator -PSC- must coordinate each other in a crisis management context. Sending police vehicles and/or fire trucks (request B) to the crisis place imposes that the number of vehicles and/or trucks is setup (request A). Request A must occur before request B to avoid a meaningless business workflow.

Another example is the coordination of actions within a device: actions (a, x,y, z, and w) are launched in reaction of events (go, request_c,request_d, request_e, request_f). request_g, and request_h). Effects (or not) depend upon states and possible guards on state transitions.

Example (SCXML model)

X-STATE-based implementation

interface Local_variables {
    counter: number;
}

const my_device: any = X_STATE.createMachine<Local_variables>({
        context: {
            counter: 0
        },
        id: 'My_device',
        initial: 'Idle',
        predictableActionArguments: true, // Default option from v. 5.x...
        states: {
            Idle: {
                id: 'Idle',
                on: {
                    go: 'Busy'
                }
            },
            Busy: {
                on: {
                    request_c: {
                        target: ['Busy.S1.S12', 'Busy.S2.S22'],
                    },
                    request_d: {
                        target: 'Busy.S2.S21',
                    },
                    request_e: {
                        target: 'Idle',
                    }
                },
                type: 'parallel',
                states: {
                    S1: {
                        initial: 'S11',
                        on: {
                            request_b: 'S1.S12'
                        },
                        states: {
                            S11: {
                                entry: ['w'],
                                exit: ['x'],
                            },
                            S12: {
                                entry: ['y'],
                                exit: ['z'],
                                on: {
                                    request_g: 'S11'
                                }
                            }
                        }
                    },
                    S2: {
                        initial: 'S22',
                        states: {
                            S21: {
                                // https://xstate.js.org/docs/guides/transitions.html#faq-s
                                on: { // Use of state id.:
                                    request_f: '#Idle'
                                }
                            },
                            S22: {
                                entry: {type: 'xstate.raise', event: {type: "request_h"}}
                                // To be avoided: 'entry: () => instance.send("request_h")'
                            }
                        }
                    },
                    S3: {
                        type: 'parallel',
                        states: {
                            S31: {
                                entry: () => console.info("Entering 'S31'..."),
                                on: {
                                    request_h: {
                                        target: 'S31',
                                        actions: 'a' // https://xstate.js.org/docs/guides/actions.html#api
                                    }
                                },
                            },
                            S32: {}
                        }
                    }
                }
            }
        }
    },
    {
        actions: {
            a: () => console.info('\ta'),
            w: () => console.info('\tw'),
            x: () => console.info('\tx'),
            y: () => console.info('\ty'),
            z: () => console.info('\tz')
        },
    });

const instance = X_STATE.interpret(my_device).onTransition((state, event) => {
    console.log(state.value);
    console.log(event.type);
});
instance.start();
instance.send("go");

Implementation of allowed events: here