✨ Worker extensible pour Node.js fonctionnant avec les plates-formes AWS Step function et Camunda BPM optimisées par TypeScript ✨
Nous avions besoin d'un framework pour nous aider à développer rapidement des Workers. Ces derniers sont utilisés pour exécuter des tâches.
Ce framework offre les avantages suivants:
- Expérimenter et choisir la plate-forme Camunda, AWS Step function ou d'autres sans réécrire la logique métier.
- Au lieu de dépendre directement d'un client Camunda, ce projet fournit une couche d'abstraction. De cette façon, il est plus facile de changer de client ou de créer le vôtre.
- Vous voulez avoir une standardisation des workers.
- L'uniformisation. En effet, vous pouvez utiliser plusieurs plates-formes en fonction des besoins du projet.
- Ajout de fonctionnalités comme l'automatisation des traces (incluant la propagation).
- Ce Framework impose la parité des fonctionnalités entre AWS Step function et Camunda BPM via les bibliothèques clientes. Certaines fonctionnalités exposées à la plate-forme Camunda BPM ne sont pas présentes dans ce package car nous ne pourrions pas les fournir si nous passons à AWS Step function. Cette limitation est destinée à guider les développeurs dans la préparation de la migration.
- La documentation est disponible dans ce dossier
- Une documentation complète sur l'API est disponible en ligne et dans le sous répertoire
docs
Librairie | Description |
---|---|
workit-types | Cette librairie fournit les interfaces / enums TypeScript pour les classes de Workit |
workit-core | Cette librairie fournit les implémentations par défaut de la librairie "Workit types" |
Librairie | Description |
---|---|
workit-bpm-client | Ce module fournit un contrôle complet pour intéragir avec la plateforme Camunda Bpm. Il utilise camunda-external-task-client-js par défaut. |
workit-stepfunction-client | Ce module fournit un contrôle complet pour intéragir avec la plateforme Step functions. Par défaut, il utilise @aws-sdk/client-sqs , @aws-sdk/client-sfn . |
npm i @villedemontreal/workit
ou utiliser le générateur en dessous
Basculer entre les plateformes est simple, il suffit de spécifier un TAG
pour l'IoC.
const worker = IoC.get<Worker>(CORE_IDENTIFIER.worker, TAG.camundaBpm);
worker.start();
worker.run();
const manager = IoC.get<IWorkflowClient>(CORE_IDENTIFIER.client_manager, TAG.camundaBpm);
const fullpath = `${process.cwd()}/sample/BPMN_DEMO.bpmn`;
await manager.deployWorkflow(fullpath);
const manager = IoC.get<IWorkflowClient>(CORE_IDENTIFIER.client_manager, TAG.camundaBpm);
await manager.getWorkflows()
const manager = IoC.get<IWorkflowClient>(CORE_IDENTIFIER.client_manager, TAG.camundaBpm);
await manager.getWorkflow({ bpmnProcessId: "DEMO" });
const manager = IoC.get<IWorkflowClient>(CORE_IDENTIFIER.client_manager, TAG.camundaBpm);
await manager.createWorkflowInstance({
bpmnProcessId: "MY_BPMN_KEY",
variables: {
hello: "world"
}
});
Vous pouvez définir plusieurs tâches pour un seul Worker. Il traitera tous les messages et acheminera les requêtes vers les bonnes tâches.
export class HelloWorldTask extends TaskBase<IMessage> {
// You can type message like IMessage<TBody, TProps> default any
public execute(message: IMessage): Promise<IMessage> {
const { properties } = message;
console.log(`Executing task: ${properties.activityId}`);
console.log(`${properties.bpmnProcessId}::${properties.processInstanceId} Servus!`);
// put your business logic here
return Promise.resolve(message);
}
}
enum LOCAL_IDENTIFIER {
// sample_activity must match the activityId in your bpmn
sample_activity= 'sample_activity'
}
// Register your task
IoC.bindTo(HelloWorldTask, LOCAL_IDENTIFIER.sample_activity);
Vous pouvez même faire des liaisons complexes comme
IoC.bindTask(HelloWorldTaskV2, LOCAL_IDENTIFIER.activity1, { bpmnProcessId: BPMN_PROCESS_ID, version: 2 });
const worker = IoC.get<Worker>(CORE_IDENTIFIER.worker, TAG.camundaBpm);
// or TAG.camundaBpm
worker.once('starting', () => {
// slack notification
});
worker.once('stopping', () => {
// close connections
});
worker.once('stopped', () => {
// slack notification
});
const handler = worker.getProcessHandler();
handler.on('message', (msg: IMessage) => {
// log/audit
});
handler.on('message-handled', (err: Error, msg: IMessage) => {
if (err) {
// something wrong
} else {
// everything is fine
}
});
worker.start();
worker.run(); // Promise
worker.stop(); // Promise
const workerConfig = {
interceptors: [
async (message: IMessage): Promise<IMessage> => {
// do something before we execute task.
return message;
}
]
};
IoC.bindToObject(workerConfig, CORE_IDENTIFIER.worker_config);
Par défaut, nous lions un NoopTracer
mais vous pouvez en fournir un et il doit étandre Tracer. Nous vous recommandons fortement d'utiliser ce type de pattern dans vos tâches : le pattern "Domain Probe". Mais voici un exemple :
// Simply bind your custom tracer object like this
IoC.bindToObject(tracer, CORE_IDENTIFIER.tracer);
export class HelloWorldTask extends TaskBase<IMessage> {
private readonly _tracer: Tracer;
constructor(tracer: Tracer) {
this._tracer = tracer
}
public async execute(message: IMessage): Promise<IMessage> {
const { properties } = message;
console.log(`Executing task: ${properties.activityId}`);
console.log(`${properties.bpmnProcessId}::${properties.processInstanceId} Servus!`);
// This call will be traced automatically
const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
// you can also create a custom trace like this :
const currentSpan = tracer.getCurrentSpan();
const span = this._tracer.startSpan('customSpan', {
parent: currentSpan,
kind: SpanKind.CLIENT,
attributes: { key: 'value' },
});
console.log();
console.log('data:');
console.log(response.data);
// put your business logic here
// finish the span scope
span.end();
return Promise.resolve(message);
}
}
Vous pouvez consulter le dossier sample
où nous fournissons un exemple (parallel.ts) en utilisant Jaeger.
Voir le tutoriel relié aux traces
TODO: show for step function
Par défaut, nous définissons une stratégie simple de réussite ou d’échec. Nous vous recommandons vivement de fournir la vôtre car votre application déclenche des exceptions spécifiques. Les stratégies sont automatiquement traitées. Si une exception se dégage de la tâche, une stratégie d'échec est invoquée, sinon c'est un succès et la stratégie de succès est invoquée.
// the idea is to create your own but imagine that your worker works mainly with HTTP REST API
class ServerErrorHandler extends ErrorHandlerBase {
constructor(config: { maxRetries: number }) {
super(config);
}
public isHandled(error: IErrorResponse<IResponse<IApiError>>): boolean {
return error.response.status >= 500;
}
public handle(error: IErrorResponse<IResponse<IApiError>>, message: IMessage): Failure {
const retries = this.getRetryValue(message);
return new Failure(error.message, this.buildErrorDetails(error, message), retries, 2000 * retries);
}
}
// You got the idea...
// You could create also
// BadRequestErrorHandler
// TimeoutErrorHandler
// UnManagedErrorHandler
// ...
// Then you could build your strategy
/// "FailureStrategy" implements "IFailureStrategy", this interface is provided by workit
const strategy = new FailureStrategy([
new AxiosApiErrorHandler(errorConfig, [
new BadRequestErrorHandler(errorConfig),
new TimeoutErrorHandler(errorConfig),
new ServerErrorHandler(errorConfig),
new UnManagedErrorHandler(errorConfig),
//...
]),
new ErrorHandler(errorConfig)
]);
// worker will use your new strategy
IoC.bindToObject(strategy, CORE_IDENTIFIER.failure_strategy);
Nous utilisons Jest.
npm test
- camunda-external-task-client-js - client nodejs pour Camunda BPM
- @aws-sdk/client-sqs - client nodejs pour recevoir les messages de la file d'attente
- @aws-sdk/client-sfn - client nodejs pour gérer l'état des processus
- inversify - injection de dépendance
- opentelemetry - ajouter de l'instrumentation aux opérations
- Autorisez les développeurs Javascript à écrire du code conforme aux principes SOLID.
- Faciliter et encourager l’adhésion aux meilleures pratiques de POO et d’IoC.
- Ajoutez le moins de temps système possible.
docker run -d --name camunda -p 8080:8080 camunda/camunda-bpm-platform:latest
// Go: http://localhost:8080/camunda - user/password : `demo/demo`
Nous utilisons SemVer pour la gestion des versions. Pour les versions disponibles, voir les balises sur ce référentiel.
workit | AWS Step function | Camunda BPM |
---|---|---|
>=6.0.0 | tous | 7.6 to latest |
Voir aussi la liste des contributeurs ayant participé à ce projet.
Veuillez lire CONTRIBUTING.md pour plus de détails sur notre code de conduite et sur le processus de soumission des demandes.
Ce projet est sous licence MIT - voir le fichier LICENSE pour plus de détails