Não é difícil encontrarmos situações aplicáveis para processamentos assíncronos: processamento de pagamento, anti-fraude, validações com sistemas terceiros, etc. Em geral, são serviços que demandam integrações ou pode demorar tempo consideravelmente alto para uma requisição síncrona.
Neste artigo, usando o contexto de processamento de pagamentos, pretendo abordar uma forma de implementação de retorno em um processamento assíncrono. O foco, na verdade, é simular uma experiência síncrona, em que na mesma página o usuário esperaria o fim do processamento.
Certamente existem formas diferentes de obter um resultado parecido. Inicialmente podemos pensar em um polling, em que o cliente faria várias requisições para saber o status do processamento até estar finalizado. Apesar de ser de fácil implementação essa abordagem tem o ponto negativo pela quantidade de requisições necessárias, que pode ser grande se o processo for demorado.
Criei um projeto bem simples que lista cada processo e seu status. O processo na verdade é um timer aleatório entre 0 e 10 segundos que finaliza com um status também aleatório “sucesso” ou “falha”.
Para esta implementação vamos utilizar a feature de inscrição do GraphQL, o “subscription”, que nada mais é que uma abstração usando Web Socket. Uma sistema de pub/sub, como o Redis, nos permitirá notificar o retorno do processamento de forma desacoplada. Além deles, estou usando o ActiveMQ (que tem a mesma interface do SQS) como mecanismo de fila, que pode até mesmo ser substituído pelo Redis.
É importante termos um identificador do processo (task id) já que o mesmo processo deve passar por diferentes sistemas e etapas. Aqui optei por Client Generated ID, um UUID gerado no front-end que passarei a chamar de “task id”.
A API GraphQL possui dois recursos: a mutation initiatePayment
que inicializará o processamento e uma subscription paymentProcessed
que será usada para escutar o retorno do processamento.
O front-end requisitará esses recursos quase ao mesmo tempo, dando início e já escutando o retorno da mensagem criada.
1 | type Mutation { |
A mutation é bem simples. Ela envia para uma fila SQS as informações do processo criado.
Esta fila, por sua vez, é consumida pelo “Payment Processor” que gera os resultados aleatórios. O resultado do processamento é publicado no Redis Pub Sub em um tópico com nome “PAYMENT_PROCESSED.[task-id]”. Isso é o que tornará possível uma escuta reativa a este resultado, pois o cliente estará inscrito neste tópico.
Para que o front-end saiba que o pagamento foi processado e conheça o resultado desse processamento ele se inscreve na subscription “paymentProcessed” passando o task-id. A implementação desta subscription é tão simples que tem praticamente uma linha:
O método asyncIterator
do graphql-redis-subscriptions cria uma inscrição no Redis ao tópico informado e escuta os eventos publicados neste tópico, retornando um evento quando ele existir. O nome do tópico é o mesmo que o processador utilizará quando finalizar a execução.
Dessa forma o handler da subscription será chamado no front-end e ele reagirá de acordo mostrando o resultado do processamento.
Os códigos utilizados nesta demonstração são bem simples e fáceis de reproduzir. Eles estão todos disponíveis no meu GitHub neste endereço.
Este trabalho está licenciado com uma Licença Creative Commons - Atribuição-NãoComercial 4.0 Internacional.