# Entendendo Programações Reativas e o Padrão Event-Driven Architecture (EDA) ## Já um grande Desafio a ser Quebrado #### No mundo da programação e arquitetura de sistemas, conceitos como Programações Reativas e o padrão Event-Driven Architecture (EDA) têm se tornado cada vez mais relevantes. Esses paradigmas oferecem abordagens robustas para lidar com sistemas distribuídos e de alta escalabilidade, enfocando a eficiência e a adaptabilidade. Neste artigo, exploraremos os fundamentos destes conceitos e como eles podem ser aplicados para melhorar o design e a funcionalidade de aplicações modernas. #### A programação reativa é uma abordagem para desenvolver sistemas que são mais flexíveis, mais responsivos e mais resilientes diante de altas cargas ou falhas. Isso é realizado através da criação de #### fluxos de dados que reagem a mudanças, ao invés de serem conduzidos por uma sequência fixa de ações. Introduzindo o Spring WebFlux #### O Spring WebFlux é um módulo do projeto Spring que proporciona uma abordagem reativa para o desenvolvimento de aplicações web. Em contraponto ao Spring MVC, que usa uma abordagem baseada no modelo servlet, o WebFlux opera sobre uma base reativa, permitindo que as aplicações lidem com um grande número de conexões simultâneas com menor uso de recursos. ## Programações Reativas Programações Reativas são um paradigma de programação orientado a dados e propagação de mudanças. Isso significa que, em vez de escrever código que puxa dados de uma fonte e faz algo com eles, o código reage automaticamente à geração desses dados. Este paradigma é particularmente útil em situações onde os dados são atualizados em tempo real, como em interfaces de usuário, onde o estado pode mudar frequentemente e de maneira imprevisível. O coração da programação reativa é o conceito de streams de dados e a propagação de mudanças. Quando um valor muda em um ponto do programa, essa mudança é automaticamente propagada pelos streams para outras partes do programa que dependem desse valor. Frameworks reativos, como RxJS no JavaScript, oferecem abstrações poderosas para criar e manipular esses streams de dados. Padrão Event-Driven Architecture (EDA) EDA, ou Arquitetura Orientada a Eventos, é um padrão de design que promove a produção, detecção, consumo e reação a eventos. Em um sistema baseado em EDA, os componentes do sistema operam de forma desacoplada e se comunicam principalmente por meio do envio e recebimento de eventos. Isso torna os sistemas baseados em EDA ideais para ambientes distribuídos e escaláveis, onde a capacidade de responder de forma ágil e adaptável às mudanças é crucial. O padrão EDA é frequentemente utilizado em sistemas de microserviços, onde diferentes serviços operam de forma independente, mas precisam comunicar-se eficientemente. Ao utilizar eventos para essa comunicação, os serviços podem permanecer desacoplados, aumentando a flexibilidade e a escalabilidade do sistema como um todo. Integração entre Programações Reativas e EDA A combinação de Programações Reativas com EDA oferece um poderoso modelo para construir sistemas responsivos e escaláveis. Em tal integração, os eventos gerados em um sistema EDA podem ser tratados como streams de dados em um modelo de programação reativo. Isso permite que os sistemas reajam em tempo real a eventos, processando e respondendo de maneira eficiente e desacoplada. Por exemplo, em uma aplicação de e-commerce, um evento de "Pedido Criado" pode desencadear uma série de reações em diferentes partes do sistema - desde o processamento do pagamento até a logística de envio. Utilizando Programações Reativas em conjunto com EDA, cada serviço pode reagir de forma assíncrona e eficiente a esses eventos, mantendo o sistema ágil e fácil de manter. Diferenças entre Aplicações Não Reativas e Aplicações Reativas Aqui estão algumas das principais diferenças: Modelo de Thread Não Reativa: Geralmente baseada em um modelo de thread por requisição. Cada requisição é alocada para uma thread, que pode ficar bloqueada se estiver esperando por um recurso ou operação. Reativa: Usa um modelo de thread não bloqueante. A thread pode atender múltiplas requisições, pois ela não fica bloqueada esperando por operações de I/O ou outros recursos. ### Uso de Recursos #### Não Reativa: Pode ter um alto consumo de recursos, especialmente sob alta carga, devido ao grande número de threads. Reativa: Mais eficiente no uso de recursos, pois utiliza menos threads, reduzindo o overhead de context switching e uso de memória. Escalabilidade Não Reativa: Pode enfrentar desafios de escalabilidade quando há muitas requisições simultâneas, devido ao bloqueio de threads. Reativa: Projetada para alta escalabilidade, capaz de lidar com muitas conexões simultâneas. Padrão de Código Não Reativa: Geralmente imperativa, seguindo um fluxo sequencial de execução. Reativa: Baseada em fluxos de dados e eventos, geralmente utilizando bibliotecas como Reactor ou RxJava.ivos e robustos. O que é a Web Reativa? A programação reativa é uma abordagem para desenvolver sistemas que são mais flexíveis, mais responsivos e mais resilientes diante de altas cargas ou falhas. Isso é realizado através da criação de fluxos de dados que reagem a mudanças, ao invés de serem conduzidos por uma sequência fixa de ações. ## Introduzindo o Spring WebFlux O Spring WebFlux é um módulo do projeto Spring que proporciona uma abordagem reativa para o desenvolvimento de aplicações web. Em contraponto ao Spring MVC, que usa uma abordagem baseada no modelo servlet, o WebFlux opera sobre uma base reativa, permitindo que as aplicações lidem com um grande número de conexões simultâneas com menor uso de recursos. ## Diferenças entre Aplicações Não Reativas e Aplicações Reativas Aqui estão algumas das principais diferenças: Modelo de Thread ### Não Reativa: Geralmente baseada em um modelo de thread por requisição. Cada requisição é alocada para uma thread, que pode ficar bloqueada se estiver esperando por um recurso ou operação. Reativa: Usa um modelo de thread não bloqueante. A thread pode atender múltiplas requisições, pois ela não fica bloqueada esperando por operações de I/O ou outros recursos. ### Uso de Recursos #### Não Reativa: Pode ter um alto consumo de recursos, especialmente sob alta carga, devido ao grande número de threads. Reativa: Mais eficiente no uso de recursos, pois utiliza menos threads, reduzindo o overhead de context switching e uso de memória. Escalabilidade Não Reativa: Pode enfrentar desafios de escalabilidade quando há muitas requisições simultâneas, devido ao bloqueio de threads. Reativa: Projetada para alta escalabilidade, capaz de lidar com muitas conexões simultâneas. Padrão de Código Não Reativa: Geralmente imperativa, seguindo um fluxo sequencial de execução. Reativa: Baseada em fluxos de dados e eventos, geralmente utilizando bibliotecas como Reactor ou RxJava. A programação reativa possui o foco principal em trabalhar com a propagação de mudanças no fluxo de dados de forma assíncrona aumentando sua capacidade responsiva. Ao tornar-se responsiva (Real-time), nossa aplicação precisa saber reagir a mudanças rapidamente, seja de carga (Load) ou orquestração a serviços externos (Downstream Services). Tornar-se reativo representa adquirir agilidade sobre qualquer mudança que afete nossa habilidade de atender uma demanda. Para nos tornarmos responsivos, precisamos direcionar nossos esforços rumo a elasticidade (Elastic), pois determinada característica permite ao sistema aumentar seu poder de processamento mantendo os índices de latência reduzidos. Ao mesmo tempo em que a responsividade possui como característica principal a baixa latência, temos por outro lado a elasticidade exercendo da mesma forma grande influência sobre latência. Neste contexto, compreendemos o termo elasticidade como a habilidade de lidar com a carga de trabalho (Workload), aumentando a capacidade de processamento em alta demanda e reduzindo a capacidade de recursos em baixa demanda. **Reativo = Resiliente + Responsivo + Elástico** Ao construirmos sistemas reativos (Reactive Systems) promovemos o aumento de nossa capacidade responsiva. A responsividade acrescenta maior qualidade na experiência do usuário final. Desta forma, quanto maior a responsividade melhor será a experiência (Feeling + Feedback) sobre os componentes que entregaremos. Além disso, melhor será a capacidade de processamento (Throughput) em menor espaço de tempo. O Throughput replica a responsividade como resultado de ações tomadas em direção ao melhor tratamento do processo de carga atingindo maior elasticidade. Estratégias como escalabilidade horizontal e vertical servem como alternativas para aumento de elasticidade da aplicação. Um sistema verdadeiramente responsivo apresenta naturalmente aspectos pertencentes ao Manifesto Reativo, possuindo características essenciais como Responsividade, Elasticidade (Resizing Load), Tolerância a falhas (Resilient), Isolamento e Escalabilidade (Horizontal ou Vertical), estando embarcado em uma arquitetura de comunicação Message-driven. Outro conceito importante no contexto de responsividade trata-se da resiliência, sendo a característica de permanecer responsivo perante as falhas. Sem dúvida, o gerenciamento da resiliência trata-se de um critério de avaliação da qualidade, onde componentes funcionalmente resilientes são independentes e recuperam-se de suas falhas. Desta forma, qualquer aplicação que utilize o paradigma reativo adquire todas as vantagens citadas como competitiva ao negócio. Estes atributos são conquistados na prática empregando o princípio de comunicação Message-driven como estrutura base em sistemas distribuídos, referindo-se ao design de arquitetura e infraestrutura dos componentes. Threads: Chamadas Síncronas e Assíncronas Construir sistemas distribuídos e escaláveis trata-se de um enorme desafio. Mesmo empregando paradigmas Funcionais e Reativos, presentes em linguagens de programação (Java, Scala, Clojure) e Libraries (RxJava, Reactor, Akka Streams), amenizando a complexidade e curva de aprendizado, precisamos ainda nos preocupar com as naturezas de comunicação, modelo de retorno assíncrono não bloqueante, modelo de Threads, etc. O comportamento das Threads em um modelo de processamento síncrono são bloqueados pelo I/O, não podendo atender outras requisições no mesmo período em que aguardam o retorno da chamada anterior. No processamento de requisições síncronas uma fatia dos recursos de CPU fica por conta de latências intrinsecamente relacionadas à chamadas bloqueantes. Operações que gerenciam com mais eficiência recursos de I/O utilizam modelos de interação assíncronos e não bloqueantes, pois o fator tempo é abordado com maior eficiência. Para trabalharmos com sistemas distribuídos, com design de arquitetura mais escalável e simplificado é necessário aplicar um estilo de comunicação baseada em eventos (Event-driven Communication). No contexto reativo, conceitos como Streaming e Backpressure (contrapressão) compõem a base para compreendermos aspectos essenciais inerentes ao paradigma. Pipelines: Streams Assíncronos com Backpressure O conceito de Streaming trata-se de uma eficiente técnica arquitetural a qual explora modelos de construção de fluxos de dados, reagindo a eventos na linha do tempo. Seu processamento e transformações de dados são realizadas em um Pipeline composto de Steps, conhecidos como Processors. O conceito de Backpressure refere-se a um mecanismo para gerenciamento de carga entre os passos em execução do pipeline em relação às chamadas aos Upstream Services (Publishers), impedindo sobrecarga entre os componentes (Subscribers). Aplicações que utilizam um estilo de arquitetura com processamento de Streams — Reativos podem utilizar Windowing (janela de processamento) para atender o fluxo de forma inteligente, evitando Streams de dados infinitos. Pipelines reativos podem ser empregados em sistemas distribuídos com processamento multi-core. A compreensão do conceito reativo, do ponto de vista de infraestrutura, é importante para lidarmos com as complexidades como gerenciamento de Threads, recursos por I/O, assincronicidade com Callbacks, etc. Deste modo, mesmo Frameworks reativos (RxJava, Reactor) responsabilizando-se em gerenciar complexidades de hardware/infra, possuir conhecimentos em um nível mais baixo poderá fazer a diferença no sucesso de nossa aplicação. O princípio Message-driven Pattern sugere que obrigatoriamente a comunicação deva ser não bloqueante. Técnicas de Callback podem ser aplicadas minimizando o bloqueio de recursos (Threads), sendo o resultado da execução devolvido posteriormente através de uma nova thread resgatada do pool, adquirindo um baixo acoplamento entre Caller Service e Called Service durante o processamento da requisição. Uma API assíncrona oferece benefícios em relação a APIs síncronas, otimizando recursos de execução em Threads separadas. As funções de Callback são responsáveis por informar a conclusão do processamento de uma requisição. A programação assíncrona dos nossos fluxos permitem aos componentes serem mais ágeis e independentes, tornando-se mais concisos e menores facilitando sua manutenção. JVM java.util.concurrent.Future: O Princípio A interface Future, inserida a partir do Java 5, foi um passo importante para a programação assíncrona para JVM-Based Languages.. com a desvantagem de que, caso a requisição ainda não tenha sido finalizada (Upstream Service Call), sua instância implementando Future bloqueará a Thread até o término da execução (bloqueio de I/O). Um dos principais conceitos promovidos por fluxos reativos são as Non-Blocking Operations. Sendo o oposto ao modelo Request-response (Síncrono/bloqueante). Adquirindo assincronicidade através dos eventos, nenhum recurso fica preso dentro dos pipelines reativos. O processamento de chamadas externas (Upstream Services) é gerenciado de maneira mais efetiva, pois o modelo de Threads trabalha de forma mais eficaz em relação aos recursos de máquina. As implementações da interface Future (Java 5), CompletionStage, CompletableFuture (Java 8) foram utilizadas como referência para arquiteturas orientadas à mensagens, possibilitando comunicações mais robustas. Avanços na evolução de Callbacks assíncronos foram obtidos no passado através de um melhor gerenciamento de I/O, evitando problemas relacionados ao controle manual de Threads. O Observer Pattern, conceito mais antigo a utilizar Subjects gerando eventos de notificação aos Observers inscritos, sustenta a base de programação reativa moderna, marcando o início desta revolução. Portanto, trata-se de um processo evolutivo, contendo amadurecimento de ideias e abandono de outras. Ainda assim, após ter sido pioneiro em conceitos envolvendo Non-blocking, e ter contribuído com avanços atualmente refletidos em Libraries como RxJava 1.x, as classes Observer e Observable (Async-stream) encontram-se depreciadas a partir do Java/JDK 9. Design Patterns: Observer, Publish-subscribe e RxJava O Design Pattern Observer é composto por basicamente três elementos: Observer, Observable e Subject. Sendo assim, o Subject precisa conhecer os Observers, inscrevendo-se e possibilitando que notificações, por meio de eventos, sejam enviadas ao Observer. No Publish-subscribe Pattern, publicadores e inscritos não necessitam conhecer um ao outro, favorecendo o baixo acoplamento. Diversos são os Frameworks que o implementam: Como exemplo, no contexto do Spring Framework, uma implementação do Publish-subscribe Pattern é fornecida através da Annotation @EventListener. Neste caso, possibilitando o roteamento de eventos sendo gerenciados em formato Topic-based ou Content-based pelo Spring. A Library RxJava é frequentemente referenciada como uma combinação de Observer e Itarator Pattern, acrescentada a um estilo de programação funcional. RxJava trata-se de uma library para JVM, permitindo que blocos imperativos possam funcionar com Data-streams. No universo JVM, RxJava foi um dos primeiros esforços em direção a reatividade, modificando a maneira de como criamos aplicações reativas utilizando Java. Desta forma, Observer + Itarator = RxJava (Reactive Stream). Ao passo em que no RxJava, a implementação consiste de um Observable Stream, um Subscriber e uma Subscription. O Subscription comunica a intenção do Subscriber em receber eventos de um Observable (Producer). Alguns operadores, pertencentes ao RxJava, destacam-se ao permitir-nos criar fluxos de negócio com pouco código. Para citar algumas operações, RxJava .map(): Operador bastante utilizado, onde a transformação é aplicada item a item, tendo o Output Stream contendo o mesmo número de elementos que Input Stream. O RxJava .filter(), ao contrário do .map(): Pode produzir menos elementos do que ele recebeu, pois somente emite elementos que passaram em um teste de predicado. RxJava .count(): Emite a quantidade de elementos presentes no Input Stream, somente após o Input Stream original finalizar sua execução. RxJava .zip(): Recebe dois Parallel Stream aplicando uma operação de .merge() e gerando um Output Stream. Todas as funções citadas são possíveis graças ao suporte funcional (Lambdas) adicionados a Java/JVM a partir da versão 8, facilitando a criação de fluxos complexos e assíncronos utilizando programação reativa. Elasticidade e Resiliência: Comunicação Message-driven Arquiteturas baseadas em microsserviços podem aplicar fundamentos de elasticidade e resiliência, encontrando na programação assíncrona oportunidades de adoção de conceitos como: Message-driven Architecture, Event-channel, Message-broker ou Event-bus. Mesmo seguindo especificações, bibliotecas reativas podem ser similares no comportamento geral, porém diferindo-se internamente. Devido a estas discrepâncias de implementação, não seria aconselhável misturá-las em uma mesma aplicação. Com o avanço nos esforços no campo reativo, a Reactive Streams Specification foi criada visando padronizar estas implementações de Libraries que surgiam. A especificação define quatro interfaces primárias, Publisher (similar Observable), Subscriber (similar Observer), Subscription e Processor. Reactive Streams provê o controle de Backpressure (controle de contrapressão) através de Subscription, expondo por meio da Request a quantidade de dados os quais o Subscriber possui capacidade para processar. **O conceito de comunicação assíncrona não bloqueante está naturalmente presente na maior parte das linguagens modernas, sendo o suporte por meio de Frameworks/Libraries cujo objetivo seja a otimização de de hardware (threads), evitando deixá-los ociosos.** **A especificação Reactive Streams define que todos os sinais emitidos pelo Publisher e consumidos pelo Subscriber devem ser não bloqueados. Além disso, a especificação impõe uma regra de que a invocação de operações on** **(next, publish, subscribe, error, complete, etc) devem ser Thread-safe pois serão executadas por múltiplas Threads paralelamente.** Dentro de fluxos reativos, entre os Producers e Consumers, existe uma subscrição que especifica o relacionamento entre eles, gerenciando a produção e consumo de eventos de maneira flexível. A programação assíncrona promove a noção de tempo valorizando a chegada do primeiro byte de dados, de forma mais versátil, impedindo o cliente de esperar até o final do processamento, entregando melhor experiência ao usuário. O processamento de uma pipeline pode iniciar em uma thread, executar operações intermediárias em outra e finalizar sua execução em uma thread completamente diferente, provendo estratégias de imutabilidade e promovendo o desacoplamento temporal. Programação reativa refere-se a uma mudança de mentalidade em determinados aspectos, dentre os quais destacam-se: Do paradigma imperativo para funcional, de natureza síncrona para assíncrona, do modelo de fluxo Pull (modelo tradicional) para Push (modelo reativo). **Tipos de Fluxo — Reativos: Hot Streams e Cold Streams** Temos os Hot Observable: Emitindo eventos independentemente do Observer estar pronto para consumo, necessitando controle de Backpressure. Cold Observable: Referindo-se a emissão de eventos sob demanda tendo o controle do fluxo solicitados pelo Subscriber. Quando nos referimos ao paradigma reativo, mudamos o ponto de vista da maneira que construímos software. Seus conceitos fundamentais devem ser compreendidos a fundo, precisamos entender que chamadas nunca são bloqueantes em termos de recursos e I/O, que tudo são Streams com foco em transformação de dados, Streams não são Collections e possuem aspectos totalmente diferentes. Programação reativa propõe-se a programar fluxos de dados utilizando fluxos assíncronos sem bloqueio de Threads (non-Blocking), tendo suas origens no estilo de programação assíncrona tradicional em relação ao seu modelo de concorrência. Suporte Funcional Java 8: Lambdas e Imutabilidade A inclusão dos Lambdas a partir do Java 8 promoveram a programação funcional, promovendo a imutabilidade dos dados e propondo-se a resolver diversos problemas relacionados ao paralelismo e concorrência entre as Threads em execução. O fluxo reativo possui diversos In-Memory Stages, relacionados a sua execução e distribuídos entre diferentes Threads durante o processamento. Dentro do pipeline de execução In-Memory, os dados são processados de forma assíncrona sendo a comunicação efetuada por meio de mensagens. Desta forma os Processors presentes no fluxo dividem o processamento entre mensagens assíncronas com Threads independentes. Assim, podemos impor limites entre os Stages, possibilitando o paralelismo do fluxo. Os Stages realizam a comunicação centralizando as mensagens trocadas durante o fluxo em uma Queue interna, desacoplando o controle do processo entre Publisher e Subscriber sem bloqueio de recursos de máquina. Estratégias de Backpressure (contrapressão sob demanda) entram em cena quando o Producer não respeita a capacidade de consumo do Consumer, desta forma o controle da demanda de trabalho fica por conta do Subscriber (Pull-push). De forma que o Subscriber fica responsável por informar sua capacidade de trabalho ao Publisher, solicitando as próximas cargas (Pull) e impondo um controle de vazão (Push). Por parte do Publisher, este conceito evita que seu Subscriber seja inundado com eventos os quais não tenha capacidade de processar. Java 9 implementou o Reactive Streams specification (especificação de fluxos reativos), trazendo um novo Pull-push Model (Flow API), melhorando o comportamento reativo e seguindo o modelo Event-loop Style. Para seguirmos o conceito reativo em nossa aplicação, não podemos usar a abordagem tradicional Pulling-model para recebimento de dados, uma alternativa seria utilizarmos WebSockets ou Server-Sent Events (SSE). Neste caso, o recebimento de atualizações seriam enviadas automaticamente do servidor para o Browser, sincronizando os dados através do recurso EventSource (API JavaScript), o qual reconecta automaticamente com o servidor em caso de problemas com a conexão. **Spring: Project Reactor — Core e Fluxos Funcionais Project Reactor trata-se de uma Library, sendo a mais popular/moderna implementação da Reative Stream Specification. Atualmente, fazendo parte do ecossistema Spring possuindo seu foco primário em legibilidade de código sem sacrificar a performance, encorajando a composição de Workflows (fluxos de trabalho) através da cadeia de operadores em pipelines funcionais.** Desta forma, nada acontece até que um Subscriber crie uma Subscription, que irá dispará o fluxo de dados (métodos como .block() ou .subscriber() podem disparar uma inscrição por parte do Subscriber), assim a comunicação por parte da Subscription é realizada entre Subscriber e Publisher (upstream) e adicionalmente, do Publisher para Subscriber (downstream). Eventos estão em toda parte e são processados pelos Dispatchers, podendo ser síncronos ou assíncronos dependendo do tipo de implementação. Reactor 1.x possui habilidade para processamento de eventos em alta velocidade, assíncronos e não bloqueantes, integrando-se bem com Spring Framework e Netty. Reactor 1.x não possui suporte a estratégias de Backpressure, bloqueando o Publisher e necessitando fazer Pulling de eventos. Reactor 3.x pode ser usado somente com Java 8+, liderando a base reativa existente no Spring Framework. Reactor é agnóstico a concorrência, oferecendo Schedulers para processamento em paralelo do fluxo de dados no pipeline. No Project Reactor, as classes Flux e Mono são duas implementações da interface Publisher presente na Reactive Stream Specification. Containers de dados infinitos são naturais no estilo de programação reativa, nesse caso, os Subscribers podem cancelar a inscrição a qualquer momento, tornando o fluxo finito. Dentro do fluxo de processamento do Pipeline, cada Operator dentro do fluxo de dados gera um novo Stream com base no Stream de entrada. Mono possui semântica semelhante ao CompletableFuture e pode ser utilizado em seu lugar. CompletableFuture não pode completar uma execução sem emitir um valor, iniciando o processamento imediatamente sem disparo de assinatura. Flux e Mono possuem diversos métodos (Operators) para criarmos Streams com base nos dados disponíveis, consumindo elementos produzidos por uma cadeia de Streams reativos. Ao desenvolvermos nossos fluxos de dados reativos, Steps de transformação estão por toda a parte. Implementados pelos Processors (operadores), e desta forma, o mapeamento de eventos dentro de sequências reativas trata-se da forma mais natural de transformação de dados dentro do fluxo. Alguns operadores possuem maior índice de uso, popularidade e utilidade. Quando se trata da implementação em Streams reativos, operadores como .map(), .flatMap(), .concatMap(), .flatMapSequential(), etc possuem alto índice de uso. O operador .map(Function<T, R>) mapeia e transforma cada elemento presente no Source para um novo elemento no Target, criando o elemento no destino e alterando seu tipo de T para R conforme assinatura do Operator (operador). Tanto Flux quanto Mono provêm os operadores citados, possuindo comportamentos semelhantes do ponto de vista de implementação. O operador .map() pode ser utilizado tanto dentro de um Flux como de um Mono, gerando respectivamente transformações tanto de Flux<T> para Flux<R>, quanto de Mono<T> para Mono<R>. Neste contexto, Mono representa um Stream contendo um único sinal. Por outro lado, Flux é representado por um Stream contendo mais de um sinal emitido no Source. No caso de necessitarmos de concatenação em nosso Pipeline, o operador .concat() concatena todos os elementos emitidos a partir dos canais Upstream(source) de entrada, somente iniciando a concatenação dos elementos subsequentes no Stream após a finalização do elemento anterior e assim por diante. O operador .merge(), por outro lado, mistura os dados presentes nos Stream de entrada (Upstream) para o Stream de saída (Downstream). Diferente do operador .concat(), a inscrição (Subscriber) aos Upstreams Sources é realizada de forma imediata, simultaneamente, em todos os canais, não aguardando o processamento elemento a elemento como ocorre no operador .map(). O operador .zip() inscreve-se em todos os Upstreams de entrada, aguarda todos os elementos pertencentes ao Source Stream emitirem seus sinais para então combinar todos eles em um único elemento de saída (Output Stream). No Reactor, o operador .zip() pode operar não somente em Publishers reativos (Flux e Mono) mas também em objetos Iterable, disponibilizando para este propósito o operador .zipWithIterable(). O operador .flatMap(Function<T, Publisher<R>>) possui papel fundamental e crucial não somente no contexto reativo mas primeiramente no universo funcional. O .flatMap() trata-se de um dos operadores mais complexos de entender, porém, uma vez que realmente compreendemos como ele funciona, este operador acaba tornando-se uma poderosa ferramenta para auxiliar na construção de fluxos reativos. Naturalmente, o Project Reactor não poderia deixar de oferecer uma implementação deste operador, bem como algumas variações como .flatMapSequential(). O operador .flatMap() consiste logicamente em duas operações executadas em conjunto: .map() e .merge(). Portando antes de entendermos o comportamento do .flatMap(), é necessário entender o operador .merge(). Dentro do .flatMap(), a etapa de .map(), transforma cada elemento de entrada em um Stream reativo de saída com ciclo de vida próprio. O .merge() por sua vez realiza a combinação dinâmica de de todas as sequências de Stream geradas em uma nova sequência (Stream) reativa, através do qual são passados elementos do tipo R transformados. O comportamento interno dos operadores .flatMap() e .flatMapSequential() em relação aos Substreams gerados é inscrever-se instantaneamente e simultaneamente nestes novos Streams, passando a reagir aos eventos emitidos. O comportamento de inscrição instantânea do .flatMap() difere-se de operadores como .concatMap(), neste caso comportando-se de maneira oposta e somente inscrevendo-se no próximo Substream após o término (.onComplete() — onError()) do Stream antecedente. Importante observar que o operador .concatMap() preserva a ordem dos elementos de origem por conta de sua natureza sequencial. Por outro lado, o operador .flatMap() não necessariamente preserva a ordem original por utilizar internamente o operador .merge(), intercalando os elementos dinamicamente. A saída, visando obter vantagens entre o melhor dos mundos, seria utilizar o operador .flatMapSequential(), preservando a ordem dos elementos originais e realizando o controle de ordenação por meio de uma Queue (fila) interna. O principal caso de uso para o operador .flatMap() seria efetuar chamadas assíncronas não bloqueantes com promessa de retorno (Callback) via Promise (promessa). Assim, o retorno da chamada seria garantido em algum momento desconhecido no tempo, tendo seu retorno processado efetivamente em diferentes Threads de forma assíncrona, possibilitando o retorno ao fluxo principal do Pipeline a partir do Step ao qual a chamada ao .flatMap() foi realizada. Spring: Web MVC e Webflux Spring Framework, particularmente a partir da versão 5, embarcou diversos módulos úteis e poderosos, dentre os quais destaca-se o módulo WebFlux para criação de aplicações reativas. Para possibilitar a utilização do paradigma reativo dentro das aplicações que rodam na JVM, Spring implementa o Reactive Stream Specification (especificação para fluxos reativos) por meio do Project Reactor. Ficando o módulo Spring WebFlux responsável por prover suporte a construção de aplicações reativas do lado do servidor. Além disso, não necessitamos do Servlet API para rodarmos uma aplicação WebFlux, substituindo-os por Reactive Streams / HTTP, nem do Tomcat Servlet Container ao utilizarmos Netty Server. A partir da versão 3.1 do Servlet API, a implementação pelo Spring Framework suporta chamadas assíncronas dentro do módulo Web MVC, porém existem diversos problemas na implementação destas características. Nem todas as funcionalidades estão presentes na especificação non-Blocking Servlet API, por esta razão não podendo ser considerado um Framework para altas cargas de dados em Real-time. A implementação provida pelo WebFlux é capaz de processar múltiplas Async non-Blocking Calls (chamadas assíncronas não bloqueantes) paralelamente. Não existe uma opção para realizarmos chamadas non-Blocking dentro do ciclo de vida de uma Request utilizando Spring Web MVC. Além disso, o módulo não traz um HTTP Client non-Blocking Out-out-the-box (pronto para uso), como consequência a maior parte das chamadas aos serviços externos causará bloqueio de recursos (Threads por I/O). O módulo WebFlux utiliza o design existente do Web MVC, aplicando mudanças mínimas de código e potencialmente reutilizando boa parte. Utilizando WebFlux, garantimos um modelo de design reativo em direção a um módulo Web Stack, oferecendo uma experiência realmente reativa. No contexto reativo, uma das etapas mais cruciais trata-se do modelo de interação via Stream, onde ambos, cliente e servidor podem emitir e reagir por meio de fluxos de dados. Nesta tarefa, o protocolo para comunicação WebSocket destaca-se, sendo uma implementação do padrão Duplex (Client-server Communication) em uma versão melhorada, oferecida pelo Spring Framework. O WebFlux oferece a infraestrutura necessária para que cliente e servidor possam comunicar-se obtendo benefício deste modelo de comunicação (WebClient). Spring Web MVC mostrou-se ao longo dos anos um Framework estável, formando uma base sólida para criação de aplicações Web. Além disso, tornou o Spring Framework uma das mais populares soluções para desenvolvimento de Web Applications (aplicações web) que utilizam a Servlet API. Contudo, tecnologias antigas não atendem bem os requisitos de sistemas modernos, que possuem consumo intensivo de dados em Real-time (tempo real), abrindo margem para necessidade de implementações mais responsivas. Uma das principais diferenças entre os modelos Web MVC e WebFlux é que o Design do Spring Web MVC confia em uma estrutura de Servlet Container, de alta latência pois utiliza chamadas síncronas bloqueantes. No Web MVC, o componente responsável por controlar e mapear todas as requisições (HttpServletRequest) e respostas (HttpServletResponse) através de interação direta (Binding and Validation), trata-se do DispatchServlet. O módulo WebFlux substitui o modelo de comunicação síncrono presente no Web MVC através dos tipos reativos Flux, Mono, e Publisher acoplados às assinaturas dos métodos, possibilitando assim dois modelos de programação Annotation-based (modelo tradicional) e Functional-routing (componentes declarados via funções). Ao mesmo tempo em que WebFlux possui diversas similaridades com Web MVC ele acrescenta diversas outras funcionalidades. Spring Web MVC possui como característica chave o modelo de programação baseado em anotações, modelo o qual foi conservado pelo WebFlux. Web MVC necessita de uma Thread por requisição HTTP, pois trata-se de uma implementação da Servlet API. Por outro lado, o módulo WebFlux utiliza o Event-loop Pattern, herdado do NodeJS, promovendo o comportamento assíncrono, aumentando o ganho de escalabilidade e resiliência. ## Banco de Dados Reativo ## Banco de Dados Reativo NoSQL Natureza Não Relacional: NoSQL (Not Only SQL) refere-se a bancos de dados que não seguem o modelo relacional tradicional. Eles são projetados para lidar com grandes volumes de dados distribuídos. Escala Horizontal: NoSQL é conhecido por sua capacidade de escalar horizontalmente. Isso significa que você pode adicionar mais servidores para lidar com mais tráfego ou dados. Flexibilidade de Esquema: NoSQL permite um esquema dinâmico, o que significa que você pode ter documentos ou entidades com diferentes estruturas de dados dentro do mesmo banco de dados. Tipos de Dados: NoSQL suporta uma variedade de tipos de dados, como chave-valor, documentos, colunas largas, e grafos. Uso: Ideal para aplicativos que requerem grande volume de dados, processamento de dados em tempo real, e onde os requisitos de dados são variáveis ou não claramente definidos. Exemplos: MongoDB, Cassandra, Redis, Neo4j. ## Trabalhando com RxPY exploraremos a biblioteca RxPY, que é a biblioteca mais popular atualmente disponível para escrever sistemas reativos. Para começar, vamos querer definir um fluxo de entrada que posteriormente observaremos e, em seguida, acionaremos ações caso um desses bits de dados atenda a determinados critérios. ## Se o Banco de Dados For Relacional como Oracle ## Quarkus Reactive Outras necessidades de configuração foram automatizadas: o Quarkus fará algumas escolhas opinativas e suposições fundamentadas. Adicione as seguintes dependências ao seu projeto: a extensão Hibernate Reactive:io.quarkus:quarkus-hibernate-reactive a extensão do cliente SQL Reativo para o banco de dados de sua escolha; as seguintes opções estão disponíveis: ### quarkus-reactive-pg-client: o cliente para PostgreSQL quarkus-reactive-mysql-client: o cliente MySQL ou MariaDB quarkus-reactive-mssql-client: o cliente para Microsoft SQL Server quarkus-reactive-db2-client: o cliente para IBM Db2 ### quarkus-reactive-oracle-client: o cliente para Oracle Nubank usa programação reativa em grande parte de sua infraestrutura. A programação reativa é um paradigma de programação que foca na reação a eventos e na comunicação assíncrona. Imagem de Paradigma de programação reativaAbre em uma nova janela fernandofranzini.wordpress.com Paradigma de programação reativa ## O Nubank usa programação reativa para: #### Processar dados em tempo real: O Nubank usa o Kafka Streams para processar dados de transações de cartão de crédito em tempo real. Isso permite que a empresa forneça notificações push aos clientes sobre suas transações, analise o comportamento do usuário para identificar tendências e detecte fraudes. #### Construir microsserviços: O Nubank usa programação reativa para construir seus microsserviços. Isso torna mais fácil para a empresa desenvolver e implantar novos microsserviços de forma rápida e eficiente. #### Fornecer APIs: O Nubank usa programação reativa para fornecer APIs para seus clientes e parceiros. Isso permite que a empresa forneça APIs que sejam rápidas, responsivas e escaláveis. A programação reativa oferece vários benefícios para o Nubank, incluindo: #### Velocidade: A programação reativa permite que o Nubank processe dados em tempo real, o que é essencial para fornecer serviços como notificações push e análise de comportamento do usuário. Escalabilidade: A programação reativa permite que o Nubank escalar sua infraestrutura para lidar com o aumento do volume de dados. Resiliência a falhas: A programação reativa torna mais fácil para o Nubank lidar com falhas no sistema.