Tutorial de Workflow/BPM com Node.js (Camunda) – Parte 2

Este tutorial é uma continuação de meus estudos sobre desenvolvimento de processos complexos usando Node.js e a ferramenta de workflow Camunda.

No passo a passo anterior modelamos um processo de duas etapas muito simples, o que não demonstra muito bem as vantagens desta arquitetura. Claro que editar um fluxo BPMN é muito mais amigável para pessoas que não são desenvolvedoras do que ficar editando código, mas é quando o processo é complexo de verdade que entram as vantagens desta abordagem em cena.

Primeiro processo
Primeiro processo

Atividades Humanas

Por exemplo, e se uma das etapas do processo do software exige intervenção humana? Isso é facilmente resolvido adicionando uma User Task. Para fazê-lo eu vou editar o processo de enviar email mudando o sue tipo de Service Task para User Task, usando o ícone de chave inglesa quando ele está selecionado.

Human Task
Human Task

Talvez, por algum motivo estranho, após o usuário ser salvo no banco por uma API, deve ser derivado para um humano enviar um e-mail para ele, manualmente. Ou seja, o processo vai ficar preso ali até a tarefa manual ser realizada.

Toda User Task possui um encarregado. Para defini-lo, clique na User Task enviar e-mail e depois na aba direita de propriedades, definindo o assignee como sac (serviço de atendimento ao cliente).

Esse encarregado deve ter uma maneira de ver os usuários que foram salvos e que ele precisa enviar email, bem como ter uma forma de dizer que enviou este email. Aí que entram os forms do Camunda, um recurso simples porém útil para as tarefas manuais.

Para criar um form para esta Human Task, basta ir na janela propriedades dela, na aba Forms. Adicione os seguintes campos (não adicione ID no form e use o mesmo padrão de nome de variáveis JS):

  • dataEnvio (Date);
  • funcionario (String)

Agora faça deploy do seu processo (com o Camunda rodando, obviamente) e inicie um novo cadastro de usuário através do POSTMAN, como fizemos no tutorial anterior (não esqueça de deixar o worker Node.js rodando).

Postman
Postman

Quando o processo iniciar através do POST, ele vai enfileirar a etapa que vai ser consumida pelo worker. Na sequência, vai enfileirar a task para um humano fazer manualmente, que é o que nos interessa agora.

Processo aguardando humano
Processo aguardando humano

Quem é o responsável que definimos para esta atividade? O sac, mas ele não existe ainda, já que por padrão o Camunda apenas deixa criado um usuário demo.

Para criar este usuário sac (que depois vai ser usado por alguém para fazer suas tarefas manuais), vá na área de administração (http://localhost:8080/camunda/app/admin/default/#/) e clique na opção “Create New User”, que é bem auto explicativa.

Create New User
Create New User

Depois de criar o usuário, temos de dar permissão para ele ver a Task List do Camunda, onde vão cair as suas atividades manuais. Para fazer isso, ainda como admin (usuário demo), vá em Authorizations e “Create New Authorization”, dando permissão para o usuário sac acessar a Task List.

Note que está área de autorização é bem mais poderosa que isso, sendo que você pode aplicar regras a grupos, por exemplo, e mexer no acesso não apenas da Task List como do Cockpit também, caso tenham pessoas que vão fazer o monitoramento dos processos da empresa.

Agora, faça sign out do usuário demo e acesse a Camunda Task List no browser ( http://localhost:8080/camunda/app/tasklist), autenticando-se como sac. Você terá acesso à lista de tarefas que caíram para este usuário:

Task List
Task List

Ao preencher o formulário (que não colocamos validação, mas poderíamos) e clicar em Complete, o atendente do SAC destrava o processo deste usuário e ele seguirá seu fluxo normalmente.

Importante: cada vez que você faz um deploy do seu modelo no Camunda, ele cria uma nova versão do fluxo. Processos iniciados em versões anteriores se mantém nas suas versões originais. Tome cuidado!

Nestes casos, você pode migrar instâncias de um processo para outro ou então excluir instâncias em versões antigas de processo.

Tomada de Decisão

Seguindo na linha de mostrar fluxos cada vez mais complexos e portanto mais interessantes de serem feitos em uma ferramenta de BPM do que em linguagem de programação, vou falar agora de tomada de decisão, ou desvios de fluxo.

Em BPMN isso é chamado de gateways, que podem ser exclusivos, inclusivos, em paralelo ou de sincronização, mas falaremos aqui dos exclusivos, que seriam o equivalente em BPMN dos if/else que temos nas linguagens de programação. Dependendo de uma condição executamos um processo, caso contrário, executamos outro.

Modifique o seu processo para ficar igual ao abaixo, onde adicionei um gateway antes de salvar o usuário no banco, em que vamos verificar se ele é maior de idade ou não.

Gateway
Gateway

Agora, vamos adicionar a expressão condicional em cada uma das setas (fluxos) do nosso processo. Vamos começar pela seta “Sim”. Selecione ela e depois vá na aba propriedades, escolha o Condition Type para Expression e adicione a seguinte expressão “${age>=18}”, sem aspas, como mostra na imagem abaixo.

Expression
Expression

Faça o mesmo para o fluxo “Não”, mas ajustando a expressão de acordo (este padrão de expressão se chama Java Unified Expression Language).

Salve e faça o deploy deste novo processo e, para testá-lo, precisamos mudar levemente o payload do nosso HTTP POST via Postman, incluindo um campo age, como abaixo.

Postman Atualizado
Postman Atualizado

Agora você pode testar. Quando enviar requisições com idade menor de 18, o fluxo do processo será finalizado antes de salvar no banco, e isso pode ser observado no Camunda pois não irá gerar atividade humana.

Já quando você enviar requisições com idade igual ou superior, o worker vai funcionar e vai cair na fila do SAC para atividade manual.

Aplicando Regras de Negócio

Apesar dos gateways nos permitirem aplicar regras de negócio muito simples em nossos workflows, eles são bem limitados e/ou exigiriam muitos gateways para implementar regras complexas. Aí que entram as Business Rule Tasks.

Vamos modificar o nosso processo para, após a atividade manual de envio de e-mail, o cadastro cai para uma análise feita a partir de regras de negócio que iremos definir. Modifique o seu processo para que fique igual à imagem abaixo.

Business Rule Task
Business Rule Task

Edite as propriedades dessa Business Rule Task para que o Implementation seja DMN (já vamos falar disso), o “Decision Ref” seja “analisar-cadastro”, “Result Variable” para approved e “Map Decision Result” como “singleEntry”.

Business Rule Properties
Business Rule Properties

Salve tudo e faça novo deploy deste processo.

Esta etapa do processo vai esperar que uma DMN (Decision Model and Notation) exista para, dado um conjunto de dados de uma instância de processo (nosso usuário), ela confira uma série de regras que definirmos para retornar uma variável approved com true ou false (isso porque configuramos assim, poderia ser retornado um objeto mais complexo).

Para criar uma nova DMN Table a partir do menu File do Camunda Modeler. A primeira coisa que você deve fazer com esta DMN Table é definir o seu nome e o seu ID, este último exatamente igual ao que colocou na propriedade “Decision Ref” da Business Rule Task anterior. Antes de definirmos as nossas regras de negócio, vale ressaltar que fazer regras simples é muito fácil e rápido, mas regras complexas é uma espécie de arte e vou tentar te poupar trabalho aqui, mas não faço milagre, ok?

No tutorial oficial de DMN, eles mostra a regra mais simples possível, que é do input ser igual a uma das regras (linhas) da DMN. Deu match, retorna output à direita. No entanto, quando eu quis fazer algo mais elaborado, quebrei um pouco a cabeça pois minhas expressões não funcionavam. O segredo aqui é: trate o input antes de mandar pra DMN (via um worker Node, por exemplo) ou no próprio campo Input Expression, que é o que vou mostrar aqui. Isso porque a tabela de decisão aceita expressões FEEL muito limitadas e o ideal é usar apenas os matches comuns mesmo.

Sendo assim, adicione apenas um input na sua DMN clicando no sinal de ‘+’, coloque no Input Label o nome ‘bonito’ do campo (email), em Input Variable coloque o nome da variável que a DMN vai receber do estágio anterior do processo (email também), deixe o tipo de retorno como boolean e escolha a opção script para usarmos uma expressão JUEL (muito mais poderosa que FEEL) e deixe o seu input como abaixo.

Input Expression
Input Expression

Basicamente essa expressão verifica se o email de cadastro do usuário é do Gmail (contains). Quando o usuário chegar nesta etapa, ele vai passar como input seu e-mail, que se for um Gmail, vai passar true (definimos como boolean, lembra?) para DMN, caso contrário false.

Logo, agora precisamos definir que nosso Output é uma variável approved (boolean) e de apenas duas linhas na DMN para tratar esses dois cenários possíveis, como abaixo.

DMN Table
DMN Table

Faça deploy desta DMN e mande o POSTMAN disparar uma nova instância de processo no Rest Engine do Camunda para ver o que acontece com nossa instância. Sugiro que adicione um novo Gateway no final, para dar destinos diferentes conforme o usuário for approved ou não (ignore a tosquice do exemplo, tenho certeza que você consegue imaginar cenários práticos mais úteis) , como abaixo.

Processo Completo
Processo Completo

Para saber se a sua instância finalizou com aprovada ou não, você pode usar a própria API REST do Camunda para consultar o histórico de instâncias finalizadas através do endpoint http://localhost:8080/engine-rest/history/activity-instance/?processInstanceId=x onde x é o id da sua instância (tire a variável e ele irá trazer todas). Também é possível ter informações das decisões tomadas pela DMN Table na aba Decisions do Cockpit e essa é uma das diferenças da versão enterprise (paga) do Camunda, que possui um history visual bem útil.

Como você pode ter percebido, o que fazemos com DMNs pode ser feito também como BPMN, mas abordagens usando Gateways tornam-se muito mais confusas e geram diagramas enormes conforme a complexidade da tomada de decisão cresce. Se você tem muitos desvios condicionais aninhados, trocá-los por uma Business Rule Task atrelada a uma DMN pode ser uma boa opção.

Dica: o pessoal do Camunda disponibiliza um DMN Simulator online, para facilitar os testes de DMNs sem precisar fazer deploy, MAS ele é bem limitado pois apenas interpreta FEEL (JUEL é muito melhor).

Neste tutorial não usamos Node.js, mas ele é uma poderosa tecnologia (assim como Java) para usar com Camunda. Para saber mais sobre Node, confira o meu curso online.

Curso Node.js e MongoDB
Curso Node.js e MongoDB

Tutorial de Workflow/BPM com Node.js (Camunda)

Este artigo considera que você já sabe o mínimo de Node.js e HTTP, é um tutorial intermediário em programação.

Desde 2017 que atuo em ambientes corporativos e uma coisa que aprendi a respeitar nesse mundo são os BPMS, ou Sistemas de Gestão de Processos de Negócio, os populares “workflows”. Dentre eles, o Camunda se demonstrou uma excelente ferramenta com esse propósito, o de orquestrar complexos processos como originação de contas digitais, automação de processos manuais simples e complexos e mesas de análise.

Além de possuir uma versão gratuita (community) e ter uma baixa curva de aprendizado, o Camunda permite a integração com web APIs Java, Node.js e REST em geral. E é essa conectividade com web APIs Node.js que vamos explorar no tutorial de hoje, criando um workflow simples que chama uma API Node quando chega na etapa correta.

Mas antes de começar, meu intuito com este tutorial é não apenas lhe ensinar como usar esse tipo de ferramenta, mas te motivar a conhecer esse mundo dos BMPS pois é muito promissor.

Vamos lá!

1 – Instalando o Camunda

O Camunda é feito em Java e por isso roda nos principais sistemas operacionais do mercado. Você pode baixar a versão gratuita e open-source, Community Platform, no site oficial.

O zip que você baixa e deve extrair em uma pasta à sua escolha possui o motor de processos, o recurso de Tasklist para processos manuais e o Cockpit, que é a parte de monitoramento.

Além do Community Platform, baixe o Open Source Modeler neste link, que é a ferramenta visual para criação dos fluxos de trabalho.

Ambos arquivos são bem leves de baixar e de rodar. Na própria página de download de cada um deles explica como executá-los no seu sistema operacional, sendo que você deve ter o Java 1.7+ instalado na sua máquina para que eles funcionem (JRE e/ou JDK), bem como as variáveis de ambiente JAVA_HOME (se instalou JDK) ou JRE_HOME (se instalou o JRE) configuradas (quem é programador Java certamente já fez isso antes).

Para executar o motor no Windows (Community Platform), basta executar o start-camunda.bat, enquanto que em sistemas Unix, rode o start-camunda.sh. Em ambos os casos, o terminal deve exibir que o motor está rodando e o navegador deve abrir uma página do Tomcat em localhost:8080, como abaixo.

Camunda funcionando
Camunda funcionando

A partir dessa tela é possível acessar as aplicações do servidor do Camunda para gestão e monitoramento.

Já o Camunda Modeler é ainda mais simples de usar, pois para Windows e Mac basta iniciar o executável da aplicação e no Linux é outro arquivo .sh.

2 – Modelando nosso primeiro processo

Quando ele iniciar, deve apresentar uma tela como abaixo, onde vamos iniciar nosso primeiro processo (BPMN Diagram). A título de curiosidade, BPMN é a notação padronizada e mundial para fluxos de trabalho, mantido pela OMG (a mesma instituição que mantém o UML).

Camunda Modeler
Camunda Modeler

Para aprender vamos criar um processo bem simples: dado que um usuário deseje se cadastrar no sistema, vamos salvar ele no banco e depois mandar um e-mail pra ele, de boas vindas. Esse processo curto e simples deve lhe ajudar a entender como funciona o Camunda e como a integração com o Node.js pode ser feita, inclusive simplificando sua arquitetura.

Como meu intuito aqui não é lhe ensinar BPMN ou a usar a ferramenta visual do Camunda (que é auto explicativa), modele um processo como abaixo e explicarei na sequência o que faz cada caixinha.

Primeiro processo
Primeiro processo

Basicamente o círculo representa o início do processo. Os retângulos, são atividades (Tasks), sendo que usei o ícone de ferramenta pra alterar o tipo para Service Task. O último círculo é o fim do processo.

Existem diferentes maneiras de executar Service Tasks com Camunda e aqui vou ensinar a que faz mais sentido para desenvolvedores Node.js que é usando o padrão External Task.

Clique no primeiro retângulo de Service Task e depois na aba Properties Panel à direita. Em Implementation selecione External e em Topic escreva o nome da fila desta atividade como “salvar-usuario-banco”, como na imagem abaixo.

Properties Panel
Properties Panel

Faça o mesmo procedimento para o segundo Service Task (enviar-email).

Agora vamos configurar nosso processo para execução. Para fazer isso é bem simples: clique em qualquer área branca do workflow e depois na aba Properties Panel para configurar um process id, process name e marcar que ele é executável, como abaixo.

Processo Executável
Processo Executável

Lembre-se desse process-id, vamos usá-lo mais tarde para inserir usuários no workflow pela API do Camunda.

Salve o arquivo de workflow em uma pasta que você ache depois.

3 – Programando o serviço externo

O próximo passo é criar o serviço externo que vai ficar olhando para o tópico ‘salvar-usuario-banco’ que definimos no Service Task de salvar o usuário no banco. Imagine este tópico como sendo uma fila que o Camunda vai adicionando usuários para serem salvos no banco. Precisaremos de um worker Node.js que vai ficar esperando chegar usuários nesta fila para serem cadastrados.

Para criar este worker, crie um novo projeto Node.js em sua máquina com o nome ‘salvar-usuario-banco-worker’ e rode um npm init nele.

Agora, vamos instalar a biblioteca do Camunda para clientes de serviços externos em Node.js, com o comando abaixo:

Agora, dentro da pasta do seu projeto Node.js crie um arquivo worker.js como abaixo, que vai fazer long polling na fila do Camunda com o tópico apropriado.

Atenção às variáveis da task que estou pegando, que são os inputs que definimos lá atrás, na Service Task. Também preste atenção na linha que faz o console.log, é ali que você colocaria a lógica de salvar no banco de verdade.

Coloque esse worker a rodar normalmente em Node e ele deve se conectar à sua instância do Camunda (ela precisa estar rodando como falei lá atrás).

4 – Implantando o processo

Agora que modelamos o processo e criamos o worker do Serviço Externo, é hora de publicarmos o nosso processo no Camunda, para que ele passe a funcionar.

No Camunda Modeler, o botão de deploy é o mais à direita no menu (parece um botão de upload) e ao clicar nele, a janela abaixo se abre para que você configure o deploy.

Deploy
Deploy

Caso tenha feito tudo corretamente, o deploy deve ser bem sucedido. Caso contrário, a aba inferior Logs vai se abrir explicando o que houve.

Para ver se seu processo está devidamente implantado no Camunda, você pode ir no navegador e usar o Cockpit em http://localhost:8080/camunda/app/cockpit (use usuário e senha demo)

Agora para testar se nosso processo está funcionando, você deve realizar uma chamada REST ao Camunda, o que recomendo que seja feito usando o POSTMAN.

Será um POST em http://localhost:8080/engine-rest/process-definition/key/cadastrar-usuario/start (note o nome do processo em negrito) com Content-Type application/json no header e o seguinte body (raw):

Se tudo der certo, ao você enviar essa requisição, terá retorno positivo no POSTMAN, como abaixo.

Postman
Postman

Olhando no terminal que está rodando o nosso worker Node.js, temos:

Worker Funcionando
Worker Funcionando

O que mostra que passou corretamente por onde deveria.

5 – Indo além

Mas e o que aconteceu quando o usuário chegou na etapa de envio de e-mail?

Dê uma olhada no Cockpit, acessando o nosso processo Cadastrar Usuário. Você vai ver que tem um número na Service Task de enviar email indicando que tem um usuário preso ali.

Usuário preso
Usuário preso

O processo chega nessa etapa e pára, pois não implementamos nada que consuma o tópico de envio de e-mail ainda. Esse é o funcionamento básico do workflow, ele vai ficar com o usuário preso até que ele seja consumido da sua fila por algum worker Node.js.

Se clicarmos no ID desse usuário, você consegue ver os seus dados e inclusve podemos editá-los.

Para podermos avançar, você terá de implementar um novo worker, para consumir o tópico de envio de e-mails. Faça isso usando o passo-a-passo que fizemos antes e coloque a rodar o seu novo worker que deve funcionar normalmente. Se quiser, use o que ensinei de Node Mailer para enviar e-mails de verdade usando Node.js, ou então o connector de envio de e-mails do Camunda.

Tão logo você suba um worker que consuma o tópico de envio de e-mails, a fila destrava e zera novamente.

Existem muitas outras coisas bacanas que dá para fazer com Camunda + Node, como colocar regras de negócio em DMNs, fazer transbordo de atividades para seres humanos, fornecer fronts para interagir com etapas do processo, postergar ações por tempo determinado, forks, syncs, startar tasks em paralelo e muito mais.

Espero ter despertado o seu interesse pelo assunto e no futuro pretendo escrever mais sobre ele conforme fizer sentido.

Confira a segunda parte deste tutorial neste link!

Curtiu o tutorial? Quer fazer com curso online de Node.js e MongoDB comigo? Clique no banner abaixo!

Curso Node.js e MongoDB

Tutorial de validação de Input de dados em Node.js

Este tutorial requer conhecimento prévio de Node.js, que podem ser obtidos em outros artigos, em meus livros ou meu curso online, onde inclusive possui uma aula em vídeo ensinando a fazer a mesma coisa.

Validar o input/entrada de dados em aplicações é uma das poucas verdades absolutas no mundo da programação. Você simplesmente não pode, jamais, confiar que os dados enviados para sua aplicação processar estarão no formato e tipo corretos que você precisa.

Mesmo que você adicione validação na sua interface ou front-end, dificilmente você conseguirá garantir que, em algum momento, algum usuário descuidado ou malicioso acabe lhe enviando dados fora do padrão que você espera e isso pode ser desastroso.

Quando você pensa em desenvolvimento web então, nem se fala. Existem muitas formas de burlar validações colocadas no front-end, seja através de maneiras simples como desativando javascript ou inspecionando e alterando o HTML da página e muito mais, a formas mais complexas como capturando requisições HTTP do front para o backend e alterando as mesmas (Man in the Middle Attack).

No caso do Node.js, mais especificamente do web framework Express, o mais usado nesta plataforma, os dados recebidos são provenientes dos objetos body, param e query, que aceitam apenas texto de qualquer formato. Se você está fazendo web APIs RESTful, esta validação que vou ensinar neste tutorial será a sua primeira e muitas vezes única defesa contra o descuido e as más intenções de quem for utilizar as suas APIs.

Embora você possa fazer todas suas validações manualmente, usando ‘ifs’ e outras práticas básicas de programação, isso geralmente leva a muito código repetitivo, complexo de testar unitariamente e arquiteturas pouco elegantes. Usaremos neste tutorial o pacote @hapi/joi, o mais popular atualmente para validação de dados e espero gerar alguns insights de como você pode levar validação a sério na sua aplicação sem torná-la um espaguete Javascript.

Então vamos lá!

Curso Node.js e MongoDB

Vamos começar de maneira bem simples, criando um novo projeto Node.js para mostrar o básico do @hapi/joi e mais tarde espero poder trazer conceitos mais avançados em artigos futuros.

Crie uma pasta chamada node-validation no seu computador e navegue até ela via terminal para criar um projeto dentro dela, usando o npm init. Crie também uma index.js aí.

Adicione no projeto a extensão que vamos usar, com o comando abaixo:

O @hapi/joi (evolução do antigo pacote Joi) usa uma linguagem descritiva para criar schemas de validação (ao contrário de estruturas imperativas como ifs) e é um desses schemas que vamos criar no início do seu index.js, usando o código abaixo de exemplo, que será explicado a seguir.

Na primeira linha, apenas carregamos uma constante ‘Joi’ que será utilizada para criar nossos schemas de validação (através de Joi.object) e o primeiro deles será o userSchema, que possuirá todas as regras de validação de usuários cadastrados no sistema.

A primeira regra é relacionada ao username, que será uma string (Joi.string), alfanumérica (alphanum), com no mínimo 3 no máximo 30 caracteres (min e max) e que é uma propriedade obrigatória (required).

A segunda regra é no password, que também deve ser uma string mas deve seguir o padrão (pattern) declarado sob a forma de uma expressão regular que aceita de 3 a 30 letras maiúsculas, minúsculas e números.

Já a terceira regra, birth_year define que esse dado deve ser um número (Joi.number), inteiro (integer) e com o seu valor entre 1900 e 2001 (min e max).

A última regra, email, foi colocada com o intuito de mostrar o poder desta biblioteca que possui muitas funções utilitárias para facilitar a sua vida, como as já mostradas anteriormente (string, number, required, etc) e aqui temos a função email que recebe configurações por parâmetro como minDomainSegments (para definir o número de partes mínima no domínio do e-mail) e tlds (Top Level Domains – para permitir ou bloquear extensões específicas de domínio).

Agora, para usar este schema de validação é muito simples, basta chamar o objeto userSchema passando por parâmetro na function validate o objeto a ser validado, sendo importante que os nomes das propriedades coincidam.

No primeiro teste, o val1 virá com o erro vazio, pois o objeto passado por parâmetro atende aos requisitos do userSchema. Já no segundo teste, val2 terá um erro pois não foi passado o username, que é obrigatório segundo as regras que criamos (required).

Rode este projeto simples no terminal e você verá essas validações em ação.

Resultado das Validações
Resultado das Validações

Antes de prosseguir, sugiro que “desafie” as validações do Joi com outros testes que cubram mais cenários possíveis, só para ver que de fato ele funciona como deveria.

Agora que entendemos o básico, é importante que a gente separe a lógica de validação da lógica de aplicação.

Para fazer isso, crie outro arquivo na sua aplicação que chamaremos de validations.js, com o seguinte conteúdo:

Note que eu extraí a lógica de validação do index.js para o validations.js e apenas adicionei um module.exports no final, para expor o schema para fora deste módulo JS.

Agora o nosso index.js ficará bem menor, uma vez que a aplicação não faz nada exceto chamar as validações pré-configuradas.

Rode novamente esta aplicação no terminal e ela deve continuar funciona como anteriormente, mas agora com uma organização mais elegante!

E por hoje é só pessoal. Espero em próximos artigos explorar formas mais elaboradas de validação, incluindo usando middlewares do Express em aplicações web, mas tenho certeza que esta introdução já lhe deu algumas ideias de como fazê-lo.

Um abraço e sucesso!

Prof. Luiz

Curtiu o post? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!