Spring WebFlux: Guia Completo para Programação Reativa no Spring Boot
Programação

Spring WebFlux: Guia Completo para Programação Reativa no Spring Boot

Índice

  1. Introdução ao Spring WebFlux

  2. Diferenças entre Spring MVC e WebFlux

  3. Conceitos Fundamentais da Programação Reativa

  4. WebFlux e o Projeto Reactor

  5. Configuração do Spring WebFlux no Spring Boot

  6. Criando um Projeto com Spring WebFlux

  7. Trabalhando com Mono e Flux

  8. Criando APIs Reativas com WebFlux

  9. Manipulação de Erros no WebFlux

  10. Uso do WebClient para Consumo de APIs Reativas

  11. Banco de Dados Reativo com Spring Data R2DBC

  12. Testando Aplicações Reativas no Spring WebFlux

  13. Desempenho e Escalabilidade do WebFlux

  14. Casos de Uso e Aplicações Reais

  15. Conclusão

1. Introdução ao Spring WebFlux

O Spring WebFlux é o módulo reativo do Spring Framework, introduzido na versão 5, projetado para lidar com programação reativa e não bloqueante. Ele oferece uma alternativa ao tradicional Spring MVC, que opera de maneira síncrona e bloqueante. O WebFlux foi criado para oferecer maior escalabilidade, especialmente em sistemas que precisam lidar com um grande volume de conexões simultâneas, como aplicações de microservices, APIs em tempo real e sistemas baseados em eventos.

Por que usar o Spring WebFlux?

A arquitetura tradicional baseada em Servlets e threads bloqueantes apresenta limitações de escalabilidade. Em um cenário típico de Spring MVC, cada requisição ocupa uma thread do servidor até que a resposta seja enviada. Isso pode se tornar um gargalo em sistemas que lidam com milhares de requisições simultâneas.

Com o Spring WebFlux, esse problema é minimizado, pois ele utiliza um modelo de execução assíncrono baseado em eventos, permitindo que um número menor de threads gerencie um grande volume de requisições simultâneas de forma eficiente.

Principais Benefícios do Spring WebFlux

Programação reativa e não bloqueante → Usa o padrão Reactive Streams, permitindo um melhor aproveitamento dos recursos do servidor.

Menor consumo de threads → Ao contrário do Spring MVC, que cria uma thread por requisição, o WebFlux utiliza um loop de eventos para otimizar a execução das tarefas.

Escalabilidade aprimorada → Ideal para aplicações de alta concorrência, onde muitas conexões simultâneas são esperadas.

Compatibilidade com servidores Netty, Undertow e Jetty → Diferente do Spring MVC, que depende da Servlet API, o Spring WebFlux pode rodar em containers leves como Netty, reduzindo a sobrecarga do servidor.

Integração com bancos de dados reativos → O WebFlux suporta tecnologias como Spring Data R2DBC, MongoDB reativo e Cassandra reativo, garantindo melhor performance em operações com banco de dados assíncronas.

Como o Spring WebFlux Funciona?

O Spring WebFlux é construído sobre o Projeto Reactor, que fornece dois tipos principais de fluxos reativos:

  • Mono → Representa zero ou um valor assíncrono.

  • Flux → Representa uma sequência de valores emitidos ao longo do tempo.

Essas classes substituem as abordagens tradicionais baseadas em List, Optional e Future , permitindo manipulação de fluxos de dados de forma reativa.

Exemplo de uso do Mono e Flux no Spring WebFlux

import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;

public class ReactiveExample {
    public static void main(String[] args) {
        // Mono: Um único valor ou erro
        Mono<String> monoExample = Mono.just("Spring WebFlux")
                .map(String::toUpperCase);
        
        monoExample.subscribe(System.out::println); // SPRING WEBFLUX

        // Flux: Múltiplos valores emitidos ao longo do tempo
        Flux<String> fluxExample = Flux.just("Item 1", "Item 2", "Item 3")
                .map(String::toUpperCase);
        
        fluxExample.subscribe(System.out::println);
        // ITEM 1
        // ITEM 2
        // ITEM 3
    }
}

No código acima:

  • Mono representa uma operação assíncrona que retorna apenas um valor.

  • Flux representa uma sequência de valores, úteis para streams de dados contínuos.

Quando usar o Spring WebFlux?

O Spring WebFlux não substitui o Spring MVC, mas é recomendado para cenários específicos, como:

🔹 APIs reativas e não bloqueantes → Se a aplicação precisa responder rapidamente a um alto volume de requisições concorrentes, como um sistema de streaming de dados.

🔹 Microservices assíncronos → Comunicação eficiente entre serviços usando WebClient ao invés de RestTemplate.

🔹 Sistemas de IoT e mensagens → Processamento de eventos em tempo real de forma eficiente.

🔹 Banco de dados reativo → Aplicações que utilizam MongoDB Reativo, R2DBC ou Redis Reativo.

Caso a aplicação seja tradicional, baseada em operações síncronas e com baixa concorrência, o Spring MVC ainda pode ser a melhor escolha.

Conclusão

O Spring WebFlux é uma tecnologia poderosa para aplicações modernas, permitindo maior escalabilidade e eficiência no processamento de requisições. Seu modelo reativo, baseado no Projeto Reactor, possibilita um uso otimizado dos recursos do servidor, tornando-se uma alternativa ideal para APIs de alta performance e sistemas baseados em eventos.

Nos próximos tópicos, exploraremos mais sobre a diferença entre Spring MVC e WebFlux, configuração do WebFlux e como construir APIs reativas com ele. 🚀

2. Diferenças entre Spring MVC e WebFlux

O Spring MVC e o Spring WebFlux são os dois principais módulos do Spring Framework para a construção de aplicações web. Embora ambos permitam a criação de APIs REST e sistemas baseados em HTTP, eles possuem modelos de execução diferentes, o que impacta diretamente na concorrência, escalabilidade e desempenho da aplicação.

Nesta seção, vamos explorar as principais diferenças entre Spring MVC e Spring WebFlux, analisando suas arquiteturas, funcionamento interno e casos de uso ideais.

Principais Diferenças entre Spring MVC e Spring WebFlux

📌 Principais Diferenças entre Spring MVC e Spring WebFlux

1️⃣ Modelo de Execução

  • 🔹 Spring MVC: Cada requisição usa uma thread exclusiva (bloqueante).
  • 🔹 Spring WebFlux: Usa um modelo não bloqueante, baseado em eventos (event-loop).

2️⃣ Paradigma

  • 🔹 Spring MVC: Programação imperativa e síncrona.
  • 🔹 Spring WebFlux: Programação reativa e assíncrona.

3️⃣ Biblioteca Base

  • 🔹 Spring MVC: Usa a Servlet API (Tomcat, Jetty, Undertow).
  • 🔹 Spring WebFlux: Baseado no Projeto Reactor (Netty, Jetty, Undertow).

4️⃣ Manipulação de Dados

  • 🔹 Spring MVC: Trabalha com List, Optional e Future.
  • 🔹 Spring WebFlux: Usa Mono e Flux para manipular dados reativos.

5️⃣ Banco de Dados

  • 🔹 Spring MVC: Utiliza JDBC tradicional (bloqueante).
  • 🔹 Spring WebFlux: Compatível com R2DBC, MongoDB Reativo e Redis Reativo (não bloqueantes).

6️⃣ Melhor Uso

  • Spring MVC: Aplicações tradicionais, monolíticas e com baixa concorrência.
  • Spring WebFlux: Microservices, APIs de alto tráfego e sistemas escaláveis.

Agora, vamos entender melhor cada uma dessas diferenças.

1. Modelo de Execução: Bloqueante vs. Não Bloqueante

Spring MVC (Bloqueante)

No Spring MVC, cada requisição HTTP é processada por uma thread exclusiva, que aguarda até que a resposta seja completamente gerada antes de seguir para a próxima tarefa.

🔴 Problema: Em cenários de alta concorrência, o servidor pode consumir muitas threads, reduzindo a escalabilidade e causando sobrecarga.

Exemplo de controlador no Spring MVC:

@RestController
@RequestMapping("/mvc")
public class MvcController {

    @GetMapping("/hello")
    public String sayHello() {
        return "Hello from Spring MVC";
    }
}

Este método é executado de forma bloqueante, ou seja, a thread aguarda até a resposta ser enviada antes de atender outra requisição.

Spring WebFlux (Não Bloqueante)

O Spring WebFlux usa um modelo de execução assíncrono baseado em eventos, onde uma mesma thread pode lidar com múltiplas requisições simultaneamente.

🟢 Vantagem: Com menos threads, o sistema consome menos recursos, aumentando a eficiência e escalabilidade.

Exemplo de controlador no Spring WebFlux:

@RestController
@RequestMapping("/webflux")
public class WebFluxController {

    @GetMapping("/hello")
    public Mono<String> sayHello() {
        return Mono.just("Hello from Spring WebFlux");
    }
}

Aqui, o método retorna um Mono, que representa um valor assíncrono, permitindo que a requisição seja processada sem bloquear a thread.

2. Diferença no Paradigma: Imperativo vs. Reativo

O Spring MVC segue um paradigma imperativo, onde as chamadas são executadas de forma linear e síncrona. Já o Spring WebFlux adota um paradigma reativo, onde as operações são assíncronas e dirigidas a eventos.

Exemplo Prático: Buscando um Usuário no Banco de Dados

🔴 Spring MVC (Imperativo, Bloqueante)

@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
    return userRepository.findById(id).orElseThrow(() -> new RuntimeException("Usuário não encontrado"));
}

Aqui, o método findById() bloqueia a execução até que o banco de dados retorne o resultado.

🟢 Spring WebFlux (Reativo, Não Bloqueante)

@GetMapping("/{id}")
public Mono<User> getUserById(@PathVariable Long id) {
    return userRepository.findById(id)
        .switchIfEmpty(Mono.error(new RuntimeException("Usuário não encontrado")));
}

Neste caso, o retorno é um Mono<User>, o que significa que a consulta ao banco ocorre de forma assíncrona.

3. Servidores Compatíveis: Tomcat vs. Netty

  • O Spring MVC é baseado na Servlet API e geralmente roda em servidores Tomcat ou Jetty.

  • O Spring WebFlux não depende da Servlet API e pode ser executado com Netty, Undertow ou Jetty, oferecendo melhor performance para sistemas assíncronos.

Exemplo de configuração do WebFlux com Netty no application.properties:

spring.main.web-application-type=reactive
server.port=8080

Isso permite que o Spring Boot utilize um servidor reativo embutido (Netty, por padrão).

4. Manipulação de Dados: List e Optional vs. Mono e Flux

Spring MVC (Lista Simples)

@GetMapping("/users")
public List<User> getAllUsers() {
    return userRepository.findAll();
}

O método retorna uma lista completa, bloqueando a thread até que todos os usuários sejam carregados.

Spring WebFlux (Flux – Streaming de Dados)

@GetMapping("/users")
public Flux<User> getAllUsers() {
    return userRepository.findAll();
}

Com Flux<User>, os dados são enviados em tempo real, sem precisar aguardar a consulta completa. Isso é ideal para grandes volumes de dados, onde queremos processar e transmitir registros à medida que são recuperados.

5. Banco de Dados: JDBC (Bloqueante) vs. R2DBC (Reativo)

No Spring MVC, utilizamos JDBC tradicional para acessar o banco de dados, que bloqueia a thread até que a consulta seja concluída.

Já no Spring WebFlux, usamos bancos reativos como R2DBC, MongoDB e Cassandra, permitindo um acesso assíncrono aos dados.

Exemplo de Configuração para PostgreSQL com R2DBC

Adicione a dependência no pom.xml:

<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-postgresql</artifactId>
</dependency>

E configure o banco no application.properties:

spring.r2dbc.url=r2dbc:postgresql://localhost:5432/mydb
spring.r2dbc.username=admin
spring.r2dbc.password=admin

Isso permite que as operações no banco sejam reativas e não bloqueantes.

Quando Usar Spring MVC e Quando Usar Spring WebFlux?

📌 Quando Usar Spring MVC e Quando Usar Spring WebFlux?

✅ Quando Usar Spring MVC

  • 🔹 Aplicações Web tradicionais → Melhor para sistemas monolíticos e baixa concorrência.
  • 🔹 Microservices síncronos → Se os serviços se comunicam via REST síncrono, o MVC pode ser suficiente.
  • 🔹 JDBC e bancos bloqueantes → Se você usa JPA/Hibernate e JDBC, o Spring MVC se encaixa melhor.
  • 🔹 Equipes sem experiência em programação reativa → MVC tem uma curva de aprendizado menor.
  • 🔹 Projetos com baixa latência e poucas conexões simultâneas → Ideal para aplicações internas e empresariais.

🚀 Quando Usar Spring WebFlux

  • 🔹 APIs de alto desempenho e escaláveis → Ideal para sistemas que precisam lidar com milhares de requisições simultâneas.
  • 🔹 Microservices assíncronos → Comunicação eficiente entre serviços usando WebClient e Event-Driven Architecture.
  • 🔹 Bancos de dados reativos → Funciona melhor com R2DBC, MongoDB Reativo e Redis Reativo.
  • 🔹 Streaming de dados e WebSockets → Perfeito para aplicações em tempo real, como chats e monitoramento IoT.
  • 🔹 Backpressure e controle de fluxo → Se sua aplicação processa grandes volumes de dados, WebFlux permite um consumo eficiente.

Essa estrutura mantém o conteúdo organizado e acessível em qualquer dispositivo móvel, evitando rolagem horizontal e tornando a leitura mais fluida. 🚀📱

Conclusão

O Spring MVC continua sendo uma excelente escolha para sistemas tradicionais, onde a execução síncrona é suficiente. Já o Spring WebFlux brilha em cenários de alta concorrência, APIs reativas, streaming de dados e microservices assíncronos.

Ao escolher entre Spring MVC e Spring WebFlux, avalie os requisitos de escalabilidade e concorrência do seu projeto. Se sua aplicação precisa suportar milhares de conexões simultâneas e processar dados de forma eficiente, o Spring WebFlux é a melhor opção. 🚀

3. Conceitos Fundamentais da Programação Reativa

A programação reativa é um paradigma que permite o desenvolvimento de aplicações assíncronas, não bloqueantes e orientadas a eventos. Ele é amplamente utilizado no Spring WebFlux para garantir escalabilidade e eficiência no processamento de grandes volumes de requisições simultâneas.

Nesta seção, vamos entender os conceitos-chave da programação reativa e como eles se aplicam ao Spring WebFlux.

1. O Que é Programação Reativa?

A programação reativa é baseada no conceito de fluxos de dados assíncronos e propagação de mudanças. Ou seja, ao invés de trabalhar com chamadas bloqueantes, onde uma operação deve ser concluída antes de outra começar, a programação reativa permite que múltiplas operações aconteçam simultaneamente de forma eficiente.

Ela se baseia nos seguintes princípios:

  • Assincronia → As operações são executadas de forma não bloqueante.

  • Fluxo de Dados → Os dados fluem como eventos ao longo do tempo.

  • Backpressure → Controle de fluxo para evitar sobrecarga do sistema.

  • Composição Funcional → Operações encadeadas sem estados mutáveis.

Esses conceitos são fundamentais no Spring WebFlux, que utiliza Reactor para gerenciar fluxos de dados reativos.

2. O Reactive Streams e o Projeto Reactor

O Reactive Streams é uma especificação padronizada para trabalhar com fluxos de dados assíncronos em Java. Ele define quatro interfaces principais:

  • Publisher → Responsável por fornecer dados assíncronos.

  • Subscriber → Consome os dados do Publisher.

  • Subscription → Gerencia a comunicação entre Publisher e Subscriber.

  • Processor → Atua como intermediário, processando os dados entre Publisher e Subscriber.

O Spring WebFlux usa a biblioteca Reactor, que implementa o padrão Reactive Streams e fornece dois tipos principais de fluxos de dados:

📌 Diferenças entre Mono e Flux no Spring WebFlux

🔹 Mono (Fluxo de um único valor)

✅ Representa zero ou um elemento.

✅ Utilizado para buscas por ID, autenticação e operações unitárias.

✅ Métodos principais: map(), flatMap(), switchIfEmpty(), onErrorResume().

✅ Exemplo de uso:

Mono<String> monoExample = Mono.just("Spring WebFlux")
    .map(String::toUpperCase);

monoExample.subscribe(System.out::println);

🔹 Flux (Fluxo de múltiplos valores)

✅ Representa zero, um ou vários elementos ao longo do tempo.

✅ Utilizado para listas, streaming de dados e eventos contínuos.

✅ Métodos principais: map(), flatMap(), filter(), mergeWith(), zipWith().

✅ Exemplo de uso:

Flux<String> fluxExample = Flux.just("Item 1", "Item 2", "Item 3")
    .map(String::toUpperCase);

fluxExample.subscribe(System.out::println);

📌 Quando Usar Mono ou Flux?

Use Mono quando precisar de um único resultado (exemplo: buscar um usuário por ID).

Use Flux quando precisar de uma lista de elementos ou eventos contínuos.

3. Principais Características da Programação Reativa

📌 1. Assincronia e Não Bloqueio

O código não bloqueia a execução enquanto aguarda uma resposta.

🔴 Código bloqueante (Spring MVC):

public String getData() {
    return someBlockingMethod(); // Aguarda a execução
}

🟢 Código não bloqueante (Spring WebFlux):

public Mono<String> getData() {
    return Mono.fromSupplier(() -> someBlockingMethod()); // Executa sem bloquear a thread
}

📌 2. Propagação de Eventos e Streams de Dados

Diferente do modelo tradicional, onde os dados são processados apenas quando estão completos, no modelo reativo os dados são processados à medida que chegam.

Exemplo de um Flux que processa uma lista de números:

Flux<Integer> numbers = Flux.range(1, 5)
    .map(n -> n * 2);

numbers.subscribe(System.out::println);

Saída:

2
4
6
8
10

Aqui, cada número é processado de forma reativa, sem esperar a conclusão de todos os itens.

📌 3. Backpressure – Controle de Fluxo

O backpressure permite controlar a taxa de envio de dados para evitar sobrecarga no sistema.

Exemplo de um Flux com backpressure:

Flux.range(1, 100)
    .onBackpressureDrop() // Descarta valores quando sobrecarregado
    .subscribe(System.out::println);

Se a aplicação estiver processando lentamente, os valores excedentes serão descartados para evitar sobrecarga.

4. Programação Funcional no Spring WebFlux

A programação reativa utiliza operações funcionais para manipular os fluxos de dados, tornando o código mais expressivo e eficiente.

📌 Principais Operações em Flux e Mono

🔹 map() – Transformação de Dados

✅ Aplica uma função a cada elemento e retorna um novo fluxo transformado.

📌 Exemplo:

Flux<Integer> flux = Flux.just(1, 2, 3)
    .map(n -> n * 2);

flux.subscribe(System.out::println); // 2, 4, 6

🔹 flatMap() – Transformação Assíncrona

✅ Similar ao map(), mas trabalha com operações assíncronas, retornando um novo Mono ou Flux.

📌 Exemplo:

Mono<String> mono = Mono.just("WebFlux")
    .flatMap(s -> Mono.just(s.toUpperCase()));

mono.subscribe(System.out::println); // "WEBFLUX"

🔹 filter() – Filtragem de Dados

✅ Remove elementos do fluxo que não atendem à condição especificada.

📌 Exemplo:

Flux<Integer> flux = Flux.range(1, 5)
    .filter(n -> n % 2 == 0);

flux.subscribe(System.out::println); // 2, 4

🔹 mergeWith() – Combinação de Fluxos (Intercalado)

✅ Mescla fluxos diferentes, intercalando seus valores.

📌 Exemplo:

Flux<String> flux1 = Flux.just("A", "B");
Flux<String> flux2 = Flux.just("1", "2");

Flux<String> merged = flux1.mergeWith(flux2);
merged.subscribe(System.out::println); // A, 1, B, 2

🔹 zipWith() – Combinação de Fluxos (Pareado)

✅ Junta dois fluxos, combinando seus elementos um a um.

📌 Exemplo:

Flux<String> flux1 = Flux.just("A", "B");
Flux<String> flux2 = Flux.just("1", "2");

Flux<String> zipped = flux1.zipWith(flux2, (a, b) -> a + b);
zipped.subscribe(System.out::println); // A1, B2

📌 Resumo: Quando Usar Cada Operação?

Use map() → Para modificar elementos do fluxo de forma síncrona.

Use flatMap() → Para operações assíncronas que retornam Mono ou Flux.

Use filter() → Para remover elementos indesejados.

Use mergeWith() → Para misturar fluxos diferentes.

Use zipWith() → Para combinar fluxos elemento por elemento.

5. Comparação Entre Código Tradicional e Reativo

🔴 Código tradicional (Spring MVC, bloqueante)

@GetMapping("/user/{id}")
public User getUserById(@PathVariable String id) {
    return userRepository.findById(id).orElseThrow(() -> new RuntimeException("Usuário não encontrado"));
}

🟢 Código reativo (Spring WebFlux, não bloqueante)

@GetMapping("/user/{id}")
public Mono<User> getUserById(@PathVariable String id) {
    return userRepository.findById(id)
        .switchIfEmpty(Mono.error(new RuntimeException("Usuário não encontrado")));
}

No modelo reativo, o método retorna um Mono<User>, permitindo que o processamento continue sem bloquear a execução.

Conclusão

A programação reativa é a base do Spring WebFlux, permitindo criar aplicações escaláveis, assíncronas e eficientes.

Seus principais conceitos incluem:

Fluxos de dados assíncronos (Mono e Flux)

Uso do padrão Reactive Streams

Controle de fluxo (Backpressure)

Programação funcional

Com esses fundamentos, podemos construir sistemas modernos, otimizados para alta concorrência e baixa latência, aproveitando o máximo de performance do Spring WebFlux. 🚀

4. WebFlux e o Projeto Reactor

O Spring WebFlux é baseado no Projeto Reactor, uma biblioteca desenvolvida pela Pivotal que fornece suporte a programação reativa no ecossistema Spring. O Reactor implementa o padrão Reactive Streams, que define como os fluxos de dados devem ser processados de forma assíncrona e não bloqueante.

Nesta seção, exploraremos o Projeto Reactor, seus principais conceitos e como ele é integrado ao Spring WebFlux.

1. O Que é o Projeto Reactor?

O Projeto Reactor é uma biblioteca reativa para Java que fornece tipos reativos avançados para manipulação eficiente de fluxos de dados assíncronos. Ele é amplamente utilizado pelo Spring WebFlux para garantir operações não bloqueantes e escaláveis.

O Reactor implementa o padrão Reactive Streams, que define quatro interfaces essenciais:

  • Publisher → Responsável por emitir eventos.

  • Subscriber → Consome os eventos emitidos pelo Publisher.

  • Subscription → Controla a comunicação entre Publisher e Subscriber.

  • Processor → Atua como intermediário no fluxo de dados.

No Spring WebFlux, o Reactor é usado para processar dados de forma eficiente, reduzindo a necessidade de threads extras e melhorando a performance e escalabilidade da aplicação.

2. Principais Tipos Reativos no Reactor

Mono

✅ Representa zero ou um valor assíncrono.

Flux

✅ Representa uma sequência de valores emitidos ao longo do tempo.

Esses tipos permitem manipular dados de forma reativa, sem bloquear a execução.

3. Trabalhando com Mono e Flux no WebFlux

📌 Mono: Um Único Valor Assíncrono

O Mono<User> representa uma operação assíncrona que retorna no máximo um valor. Ele é utilizado quando a operação retorna um único item, como uma busca por ID no banco de dados.

🔹 Exemplo de um Mono no Spring WebFlux:

Mono<String> monoExample = Mono.just("Spring WebFlux")
    .map(String::toUpperCase);

monoExample.subscribe(System.out::println); // SPRING WEBFLUX

Aqui, o Mono.just() encapsula um valor, e o método map() aplica uma transformação sobre ele.

🔹 Exemplo de um Controller no Spring WebFlux com Mono:

@RestController
@RequestMapping("/mono")
public class MonoController {

    @GetMapping("/{id}")
    public Mono<String> getMonoExample(@PathVariable String id) {
        return Mono.just("User ID: " + id);
    }
}

Esse endpoint retorna um Mono<String>, indicando que a resposta será processada de forma assíncrona.

📌 Flux: Múltiplos Valores Emitidos ao Longo do Tempo

O Flux<T> representa um fluxo contínuo de dados, podendo conter zero, um ou múltiplos valores. Ele é útil para streams de dados, listas ou notificações assíncronas.

🔹 Exemplo de um Flux no Spring WebFlux:

Flux<String> fluxExample = Flux.just("Item 1", "Item 2", "Item 3")
    .map(String::toUpperCase);

fluxExample.subscribe(System.out::println);

Saída:

ITEM 1
ITEM 2
ITEM 3

O Flux.just() emite múltiplos valores e o map() aplica uma transformação a cada um deles.

🔹 Exemplo de um Controller no Spring WebFlux com Flux:

@RestController
@RequestMapping("/flux")
public class FluxController {

    @GetMapping("/items")
    public Flux<String> getFluxExample() {
        return Flux.just("Item A", "Item B", "Item C");
    }
}

Este endpoint retorna um Flux<String>, permitindo transmissão contínua de dados para o cliente.

4. Principais Operações com Mono e Flux

No Projeto Reactor, podemos manipular dados reativos utilizando operações como map, flatMap, filter, merge e zip.

Transformando Dados com map() e flatMap()

Mono<String> mono = Mono.just("Spring WebFlux")
    .map(String::toUpperCase);

Flux<Integer> flux = Flux.range(1, 5)
    .flatMap(n -> Mono.just(n * 2));

flux.subscribe(System.out::println);

Aqui:

  • map() transforma um único elemento.

  • flatMap() transforma elementos de forma assíncrona.

Combinando Fluxos com mergeWith() e zipWith()

Flux<String> flux1 = Flux.just("A", "B");
Flux<String> flux2 = Flux.just("1", "2");

Flux<String> merged = flux1.mergeWith(flux2);
merged.subscribe(System.out::println);

Saída:

A
1
B
2

O mergeWith() combina fluxos de forma intercalada.

Já o zipWith() combina elementos de fluxos diferentes:

Flux<String> zipped = flux1.zipWith(flux2, (a, b) -> a + b);
zipped.subscribe(System.out::println);

Saída:

A1
B2

Cada elemento é pareado com outro do segundo fluxo.

5. Tratamento de Erros no Reactor

O Spring WebFlux permite capturar e tratar erros de forma reativa.

🔹 Usando onErrorResume() para retornar um valor alternativo:

Mono<String> mono = Mono.error(new RuntimeException("Erro!"))
    .onErrorResume(e -> Mono.just("Valor Padrão"));

mono.subscribe(System.out::println); // Valor Padrão

🔹 Usando onErrorReturn() para retornar um valor fixo:

Flux<Integer> flux = Flux.range(1, 5)
    .map(n -> {
        if (n == 3) throw new RuntimeException("Erro!");
        return n;
    })
    .onErrorReturn(-1);

flux.subscribe(System.out::println);

Saída:

1
2
-1

O fluxo é interrompido e o valor -1 é retornado.

6. Backpressure – Controle de Fluxo

O Backpressure evita que o sistema seja sobrecarregado com dados excessivos.

🔹 Usando span onBackpressureDrop() para descartar elementos quando há sobrecarga:

Flux.range(1, 1000)
    .onBackpressureDrop()
    .subscribe(System.out::println);

Aqui, quando o sistema não consegue processar rapidamente, os valores excedentes são descartados.

Conclusão

O Spring WebFlux utiliza o Projeto Reactor para fornecer uma abordagem reativa, assíncrona e altamente escalável. Com os tipos Mono e Flux, podemos manipular fluxos de dados com eficiência, garantindo um melhor uso dos recursos do servidor.

🔹 Resumo dos Conceitos Chave:

Reactor implementa o padrão Reactive Streams

Mono e Flux são os principais tipos reativos

Operações reativas como map, flatMap, merge e zip

Tratamento de erros com onErrorResume e onErrorReturn

Controle de fluxo com Backpressure

Com esses conceitos, podemos construir APIs altamente performáticas e escaláveis com o Spring WebFlux! 🚀

5. Configuração do Spring WebFlux no Spring Boot

O Spring WebFlux é um dos módulos do Spring Boot que permite a criação de APIs reativas e não bloqueantes. Para utilizá-lo, precisamos configurar corretamente nosso projeto, escolher o servidor adequado e definir as dependências necessárias.

Nesta seção, vamos aprender como configurar o Spring WebFlux no Spring Boot, incluindo a escolha do servidor, dependências do projeto, configurações essenciais e exemplos práticos.

1. Criando um Projeto Spring Boot com WebFlux

O primeiro passo para configurar o Spring WebFlux é criar um projeto com as dependências corretas. Podemos fazer isso usando o Spring Initializr (start.spring.io).

Passos para Criar o Projeto:

1️⃣ Acesse Spring Initializr 2️⃣ Escolha o Spring Boot 3.x (ou versão mais recente) 3️⃣ Selecione Maven ou Gradle 4️⃣ Adicione as seguintes dependências:

  • Spring Boot Starter WebFlux

  • Spring Boot Starter Data R2DBC (se for usar banco de dados reativo)

  • Lombok (opcional, para reduzir código boilerplate) 5️⃣ Gere o projeto e extraia os arquivos no seu ambiente de desenvolvimento

2. Adicionando Dependências no pom.xml (Maven)

Se estiver utilizando Maven, adicione as seguintes dependências ao pom.xml:

<dependencies>
    <!-- Spring WebFlux -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <!-- Projeto Reactor -->
    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-core</artifactId>
    </dependency>

    <!-- Banco de Dados Reativo (Exemplo: R2DBC para PostgreSQL) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-r2dbc</artifactId>
    </dependency>
    <dependency>
        <groupId>io.r2dbc</groupId>
        <artifactId>r2dbc-postgresql</artifactId>
    </dependency>

    <!-- Lombok (Opcional) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

Se estiver usando Gradle, adicione ao build.gradle:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'io.projectreactor:reactor-core'
    implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
    implementation 'io.r2dbc:r2dbc-postgresql'
    implementation 'org.projectlombok:lombok'
}

3. Configurando o Servidor Reativo

Diferente do Spring MVC, que utiliza Tomcat por padrão, o Spring WebFlux permite escolher servidores reativos como Netty, Jetty ou Undertow.

Por padrão, se o Spring Boot Starter WebFlux for incluído no projeto, ele utilizará o Netty automaticamente. Caso queira trocar o servidor, basta adicionar a dependência correspondente no pom.xml ou build.gradle.

Exemplo: Configurar Jetty no WebFlux

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

No application.properties, podemos definir explicitamente que o aplicativo rodará em modo reativo:

spring.main.web-application-type=reactive
server.port=8080

Se você quiser usar Tomcat, remova a dependência do WebFlux e utilize spring-boot-starter-web.

4. Criando a Primeira API Reativa com Spring WebFlux

Agora que o projeto está configurado, vamos criar uma API simples usando Spring WebFlux.

📌 Exemplo de um Controller com WebFlux (Mono e Flux)

@RestController
@RequestMapping("/products")
public class ProductController {

    // Retorna um único produto (Mono)
    @GetMapping("/{id}")
    public Mono<String> getProductById(@PathVariable String id) {
        return Mono.just("Produto: " + id);
    }

    // Retorna uma lista de produtos (Flux)
    @GetMapping
    public Flux<String> getAllProducts() {
        return Flux.just("Produto A", "Produto B", "Produto C");
    }
}

🔹 Explicação:

  • O método getProductById() retorna um Mono<String>, indicando que um único valor será processado de forma assíncrona.

  • O método getAllProducts() retorna um Flux<String>, que pode conter múltiplos valores e ser processado de forma reativa.

5. Configuração do Banco de Dados Reativo (R2DBC)

Se for usar um banco de dados reativo, como PostgreSQL ou MongoDB, precisamos configurar o Spring Data R2DBC.

📌 Adicione a configuração no application.properties:

spring.r2dbc.url=r2dbc:postgresql://localhost:5432/mydatabase
spring.r2dbc.username=admin
spring.r2dbc.password=admin
spring.r2dbc.pool.enabled=true

📌 Criação de um Repositório Reativo:

@Repository
    public interface ProductRepository extends ReactiveCrudRepository<Product, String> {
}

Aqui, o ReactiveCrudRepository permite realizar operações CRUD de forma reativa, sem bloquear a execução.

6. Testando a Aplicação WebFlux

Após configurar tudo, podemos testar nossa API de forma prática.

🔹 Teste via CURL:

curl -X GET http://localhost:8080/products

Resposta esperada:

["Produto A", "Produto B", "Produto C"]

🔹 Teste via Postman:

1️⃣ Abra o Postman

2️⃣ Insira a URL: http://localhost:8080/products

3️⃣ Clique em Send

4️⃣ Verifique a resposta JSON

7. Testes Automatizados com WebFlux

Podemos testar nossa API utilizando o WebTestClient, uma alternativa ao MockMvc para aplicações reativas.

📌 Exemplo de um Teste Unitário no WebFlux:

@WebFluxTest(ProductController.class)
class ProductControllerTest {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    void testGetAllProducts() {
        webTestClient.get().uri("/products")
            .exchange()
            .expectStatus().isOk()
            .expectBodyList(String.class)
            .hasSize(3);
    }
}

🔹 Explicação:

  • WebTestClient permite testar endpoints reativos.

  • exchange() faz a requisição e valida o status HTTP.

  • expectBodyList() verifica se a resposta contém os elementos esperados.

Conclusão

✅ O Spring WebFlux permite construir APIs reativas, escaláveis e não bloqueantes.

✅ O Projeto Reactor fornece os tipos Mono e Flux para manipulação de dados assíncronos.

✅ O Spring Boot Starter WebFlux utiliza Netty como servidor padrão, mas pode ser configurado para usar Jetty ou Undertow.

✅ O Spring Data R2DBC permite trabalhar com bancos de dados reativos sem bloqueios.

WebTestClient facilita testes em aplicações reativas.

Agora você está pronto para desenvolver aplicações modernas e performáticas usando Spring WebFlux! 🚀

6. Criando um Projeto com Spring WebFlux

Criar um projeto com Spring WebFlux no Spring Boot é um processo simples e direto. Utilizando o Spring Initializr, podemos gerar um projeto com a estrutura e dependências corretas para desenvolver APIs reativas, escaláveis e não bloqueantes.

Nesta seção, veremos passo a passo como criar um projeto WebFlux do zero, adicionando dependências, configurando o servidor e implementando um CRUD reativo.

1. Criando um Projeto Spring Boot com WebFlux

A maneira mais fácil de criar um projeto com Spring WebFlux é utilizando o Spring Initializr.

🔹 Passo a Passo: Criando o Projeto

1️⃣ Acesse Spring Initializr

2️⃣ Selecione as configurações:

  • Projeto: Maven ou Gradle

  • Linguagem: Java

  • Spring Boot: Versão mais recente (3.x recomendado)

  • Dependências:

  • ✅ Spring Boot Starter WebFlux

  • ✅ Spring Boot Starter Data R2DBC (se for usar banco de dados)

  • ✅ Lombok (opcional)

3️⃣ Clique em “Generate” para baixar o projeto

4️⃣ Extraia o arquivo ZIP e abra no seu IDE favorito (IntelliJ, VS Code, Eclipse, etc.)

🔹 Estrutura inicial do projeto:

📦 my-webflux-project
 ┣ 📂 src/main/java/com/example/webflux
 ┃ ┣ 📜 WebFluxApplication.java
 ┃ ┣ 📜 controller
 ┃ ┣ 📜 model
 ┃ ┣ 📜 repository
 ┃ ┣ 📜 service
 ┣ 📂 src/main/resources
 ┃ ┣ 📜 application.properties
 ┣ 📜 pom.xml

Com essa estrutura, estamos prontos para adicionar nossas configurações e começar a codificar. 🚀

2. Adicionando Dependências ao Projeto

Se você criou o projeto manualmente, adicione as seguintes dependências no pom.xml (para Maven):

<dependencies>
    <!-- Spring WebFlux -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <!-- Banco de Dados Reativo (R2DBC com PostgreSQL) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-r2dbc</artifactId>
    </dependency>
    <dependency>
        <groupId>io.r2dbc</groupId>
        <artifactId>r2dbc-postgresql</artifactId>
    </dependency>

    <!-- Lombok (Para reduzir código boilerplate) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

Se estiver usando Gradle, adicione as dependências no build.gradle:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
    implementation 'io.r2dbc:r2dbc-postgresql'
    implementation 'org.projectlombok:lombok'
}

3. Configurando o Servidor WebFlux

Por padrão, o Spring WebFlux usa o Netty como servidor embutido. Para configurar o servidor e definir a porta da aplicação, edite o application.properties:

spring.main.web-application-type=reactive
server.port=8080

Se quiser usar Jetty ou Undertow em vez de Netty, adicione a dependência correspondente no pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

4. Criando um Modelo de Dados (Model)

Vamos criar um modelo de exemplo para representar produtos em nosso sistema.

📌 Crie a classe Product.java no pacote model

package com.example.webflux.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Table("products") // Define a tabela no banco de dados
public class Product {
    
    @Id
    private String id;
    private String name;
    private double price;
}

Aqui, usamos a anotação @Table(“products”) para mapear a entidade para uma tabela no banco de dados.

5. Criando um Repositório Reativo (Repository)

Diferente do JPA tradicional, no WebFlux usamos ReactiveCrudRepository, que permite operações CRUD de forma assíncrona.

📌 Crie a interface ProductRepository.java no pacote repository

package com.example.webflux.repository;

import com.example.webflux.model.Product;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;

@Repository
    public interface ProductRepository extends ReactiveCrudRepository<Product, String> {
}

Isso permitirá operações como findAll(), findById(), save(), sem precisar implementar manualmente.

6. Criando um Serviço Reativo (Service)

📌 Crie a classe ProductService.java no pacote service

package com.example.webflux.service;

import com.example.webflux.model.Product;
import com.example.webflux.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
@RequiredArgsConstructor
public class ProductService {

    private final ProductRepository repository;

    public Flux<Product> getAllProducts() {
        return repository.findAll();
    }

    public Mono<Product> getProductById(String id) {
        return repository.findById(id);
    }

    public Mono<Product> saveProduct(Product product) {
        return repository.save(product);
    }

    public Mono<Void> deleteProduct(String id) {
        return repository.deleteById(id);
    }
}

Aqui usamos Mono<Product> e Flux<Product> para trabalhar com dados de forma reativa.

7. Criando um Controller Reativo (API REST)

📌 Crie a classe ProductController.java no pacote controller

package com.example.webflux.controller;

import com.example.webflux.model.Product;
import com.example.webflux.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService service;

    @GetMapping
    public Flux<Product> getAllProducts() {
        return service.getAllProducts();
    }

    @GetMapping("/{id}")
    public Mono<Product> getProductById(@PathVariable String id) {
        return service.getProductById(id);
    }

    @PostMapping
    public Mono<Product> createProduct(@RequestBody Product product) {
        return service.saveProduct(product);
    }

    @DeleteMapping("/{id}")
    public Mono<Void> deleteProduct(@PathVariable String id) {
        return service.deleteProduct(id);
    }
}

Este CRUD reativo permite criar, listar, buscar e deletar produtos de forma assíncrona e não bloqueante.

8. Testando a API WebFlux

Após rodar a aplicação (mvn spring-boot:run), podemos testar os endpoints:

📌 Criar um Produto (POST)

curl -X POST http://localhost:8080/products \
     -H "Content-Type: application/json" \
     -d '{"name":"Notebook","price":2500}'

📌 Listar Produtos (GET)

curl -X GET http://localhost:8080/products

Conclusão

✅ Criamos um projeto WebFlux do zero

✅ Implementamos um CRUD reativo

✅ Usamos ReactiveCrudRepository para acessar o banco

✅ Testamos a API via cURL

Agora você pode expandir esse projeto adicionando Validações, Autenticação e Mensageria Reativa. 🚀

7. Trabalhando com Mono e Flux no Spring WebFlux

No Spring WebFlux, os tipos Mono e Flux são fundamentais para manipulação de fluxos de dados assíncronos e não bloqueantes. Eles fazem parte do Projeto Reactor e permitem que a aplicação processe dados de maneira eficiente, otimizando o uso de threads e melhorando a escalabilidade.

Neste artigo, vamos explorar como trabalhar com Mono e Flux, entender suas diferenças e ver exemplos práticos de como utilizá-los no Spring WebFlux.

1. O Que São Mono e Flux?

No Spring WebFlux, os dados são manipulados de forma reativa, utilizando Mono e Flux.

🔹 Mono (Fluxo de um único valor)

✅ Representa zero ou um elemento.

✅ Utilizado para buscas por ID, autenticação e operações unitárias.

✅ Métodos principais: map(), flatMap(), switchIfEmpty(), onErrorResume().

🔹 Flux (Fluxo de múltiplos valores)

✅ Representa zero, um ou vários elementos ao longo do tempo.

✅ Utilizado para listas, streaming de dados e eventos contínuos.

✅ Métodos principais: map(), flatMap(), filter(), mergeWith(), zipWith().

2. Trabalhando com Mono

O Mono representa uma única resposta assíncrona ou um erro. Ele é útil para operações que retornam um único objeto, como buscar um item no banco de dados ou retornar um status.

📌 Exemplo de Uso do Mono

Mono<String> monoExample = Mono.just("Spring WebFlux")
    .map(String::toUpperCase);

monoExample.subscribe(System.out::println); // SPRING WEBFLUX

🔹 Explicação:

  • Mono.just(“Spring WebFlux”) cria um fluxo reativo com um único valor.

  • map(String::toUpperCase) transforma o valor em maiúsculas.

  • subscribe(System.out::println) executa o fluxo e imprime o valor.

Mono em um Controller do Spring WebFlux

Podemos usar Mono em um controller reativo para retornar um único objeto em uma API REST.

📌 Exemplo de Controller com Mono

@RestController
@RequestMapping("/mono")
public class MonoController {

    @GetMapping("/{id}")
    public Mono<String> getMonoExample(@PathVariable String id) {
        return Mono.just("Usuário com ID: " + id);
    }
}

🔹 Explicação:

  • Mono.just(“Usuário com ID: ” + id) cria um fluxo que retorna um único valor de forma assíncrona.

3. Trabalhando com Flux

O Flux representa múltiplos valores assíncronos ao longo do tempo. Ele é útil para listas, streams de dados e notificações contínuas.

📌 Exemplo de Uso do Flux

Flux<String> fluxExample = Flux.just("Item 1", "Item 2", "Item 3")
    .map(String::toUpperCase);

fluxExample.subscribe(System.out::println);

Saída:

ITEM 1
ITEM 2
ITEM 3

🔹 Explicação:

  • Flux.just(“Item 1”, “Item 2”, “Item 3”) cria um fluxo reativo com três elementos.

  • map(String::toUpperCase) transforma cada item para maiúsculas.

  • subscribe(System.out::println) executa e imprime cada elemento.

Flux em um Controller do Spring WebFlux

Podemos usar Flux em um controller reativo para retornar múltiplos objetos em uma API REST.

📌 Exemplo de Controller com Flux

@RestController
@RequestMapping("/flux")
public class FluxController {

    @GetMapping("/items")
    public Flux<String> getFluxExample() {
        return Flux.just("Produto A", "Produto B", "Produto C");
    }
}

🔹 Explicação:

  • Flux.just(“Produto A”, “Produto B”, “Produto C”) retorna uma lista de itens de forma reativa.

4. Diferença Entre Mono e Flux

🔹 Quantidade de Elementos

Mono: Pode conter zero ou um elemento.

Flux: Pode conter zero, um ou múltiplos elementos ao longo do tempo.

🔹 Uso Principal

Mono: Ideal para buscar um único item, como um usuário por ID.

Flux: Usado para buscar listas ou processar streams de dados, como consulta de produtos ou eventos contínuos.

🔹 Tratamento de Erros

Mono: Usa onErrorResume() para capturar e substituir o erro.

Flux: Usa onErrorContinue() para ignorar elementos com erro e continuar o fluxo.

5. Operações Comuns em Mono e Flux

O Projeto Reactor permite realizar operações reativas com métodos como map, flatMap, filter, merge e zip.

Transformando Dados com map() e flatMap()

Mono<String> mono = Mono.just("Spring WebFlux")
    .map(String::toUpperCase);

Flux<Integer> flux = Flux.range(1, 5)
    .flatMap(n -> Mono.just(n * 2));

flux.subscribe(System.out::println);

🔹 Explicação:

  • map() transforma cada elemento.

  • flatMap() permite transformações assíncronas.

Combinando Fluxos com mergeWith() e zipWith()

Flux<String> flux1 = Flux.just("A", "B");
Flux<String> flux2 = Flux.just("1", "2");

Flux<String> merged = flux1.mergeWith(flux2);
merged.subscribe(System.out::println);

Saída:

A
1
B
2

O mmergeWith() combina fluxos de forma intercalada.

Já o mzipWith() combina elementos de fluxos diferentes:

Flux<String> zipped = flux1.zipWith(flux2, (a, b) -> a + b);
zipped.subscribe(System.out::println);

Saída:

A1
B2

Cada elemento é pareado com outro do segundo fluxo.

6. Tratamento de Erros em Mono e Flux

Usando onErrorResume() para retornar um valor alternativo

Mono<String> mono = Mono.error(new RuntimeException("Erro!"))
    .onErrorResume(e -> Mono.just("Valor Padrão"));

mono.subscribe(System.out::println); // Valor Padrão

Usando onErrorReturn() para retornar um valor fixo

Flux<Integer> flux = Flux.range(1, 5)
    .map(n -> {
        if (n == 3) throw new RuntimeException("Erro!");
            return n;
    })
    .onErrorReturn(-1);

flux.subscribe(System.out::println);

Saída:

1
2
-1

🔹 O fluxo é interrompido e o valor -1 é retornado.

7. Controle de Fluxo (Backpressure) com Flux

O Backpressure evita que o sistema seja sobrecarregado com muitos dados ao mesmo tempo.

🔹 Usando onBackpressureDrop() para descartar elementos quando há sobrecarga:

Flux.range(1, 1000)
    .onBackpressureDrop()
    .subscribe(System.out::println);

Se o consumidor não conseguir processar rapidamente, os valores excedentes são descartados.

Conclusão

✅ O Mono é usado para manipular um único valor ou nenhum valor.

✅ O Flux é usado para manipular múltiplos valores.

✅ Operações como map, flatMap, filter, merge e zip permitem manipular dados reativos.

✅ Métodos como onErrorResume e onErrorReturn tratam erros de forma reativa.

✅ O Backpressure evita sobrecarga ao processar grandes fluxos de dados.

Com essas técnicas, podemos criar APIs altamente performáticas e escaláveis usando Spring WebFlux e Projeto Reactor! 🚀

8. Criando APIs Reativas com WebFlux

O Spring WebFlux permite criar APIs REST reativas, não bloqueantes e escaláveis, aproveitando os recursos do Projeto Reactor. Com Mono e Flux, podemos manipular fluxos de dados de forma eficiente, melhorando o desempenho e a escalabilidade da aplicação.

Nesta seção, vamos aprender a criar uma API reativa completa com Spring WebFlux, implementando um CRUD reativo e utilizando boas práticas para controle de fluxo, tratamento de erros e testes.

1. Configurando o Projeto Spring WebFlux

Para criar uma API reativa, primeiro precisamos configurar um projeto Spring Boot com Spring WebFlux.

🔹 Dependências Necessárias (Maven)

Adicione as seguintes dependências no pom.xml:

<dependencies>
    <!-- Spring WebFlux -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <!-- Banco de Dados Reativo (Exemplo: R2DBC para PostgreSQL) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-r2dbc</artifactId>
    </dependency>
    <dependency>
        <groupId>io.r2dbc</groupId>
        <artifactId>r2dbc-postgresql</artifactId>
    </dependency>

    <!-- Lombok (Para reduzir código boilerplate) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

Se estiver usando Gradle, adicione as dependências no build.gradle:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
    implementation 'io.r2dbc:r2dbc-postgresql'
    implementation 'org.projectlombok:lombok'
}

📌 Configuração do Servidor WebFlux no application.properties:

spring.main.web-application-type=reactive
server.port=8080
spring.r2dbc.url=r2dbc:postgresql://localhost:5432/mydatabase
spring.r2dbc.username=admin
spring.r2dbc.password=admin

2. Criando um Modelo de Dados (Model)

Vamos criar uma classe para representar um Produto em nossa API.

📌 Criação da classe Product.java no pacote model

package com.example.webflux.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Table("products") // Nome da tabela no banco
public class Product {

    @Id
    private String id;
    private String name;
    private double price;
}

🔹 Explicação:

  • @Table(“products”) define a tabela do banco.

  • @Id marca o campo id como identificador único.

  • @Data (Lombok) gera automaticamente os métodos getter e setter.

3. Criando o Repositório Reativo (Repository)

O Spring WebFlux usa o ReactiveCrudRepository para trabalhar com bancos de dados de forma reativa e não bloqueante.

📌 Criação da interface ProductRepository.java no pacote repository

package com.example.webflux.repository;

import com.example.webflux.model.Product;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends ReactiveCrudRepository<Product, String> {
}

🔹 O ReactiveCrudRepository fornece métodos prontos para CRUD reativo (save, findById, deleteById, etc.).

4. Criando o Serviço Reativo (Service)

A camada de serviço gerencia a lógica de negócios da aplicação.

📌 Criação da classe ProductService.java no pacote service

package com.example.webflux.service;

import com.example.webflux.model.Product;
import com.example.webflux.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
@RequiredArgsConstructor
public class ProductService {

    private final ProductRepository repository;

    public Flux<Product> getAllProducts() {
        return repository.findAll();
    }

    public Mono<Product> getProductById(String id) {
        return repository.findById(id);
    }

    public Mono<Product> saveProduct(Product product) {
        return repository.save(product);
    }

    public Mono<Void> deleteProduct(String id) {
        return repository.deleteById(id);
    }
}

🔹 Explicação:

  • Flux<Product> retorna múltiplos produtos.

  • Mono<Product> retorna um único produto.

5. Criando o Controller Reativo (API REST)

O controller expõe os endpoints da API REST, utilizando Spring WebFlux para processar requisições de forma assíncrona.

📌 Criação da classe ProductController.java no pacote controller

package com.example.webflux.controller;

import com.example.webflux.model.Product;
import com.example.webflux.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService service;

    @GetMapping
    public Flux<Product> getAllProducts() {
        return service.getAllProducts();
    }

    @GetMapping("/{id}")
    public Mono<Product> getProductById(@PathVariable String id) {
        return service.getProductById(id);
    }

    @PostMapping
    public Mono<Product> createProduct(@RequestBody Product product) {
        return service.saveProduct(product);
    }

    @DeleteMapping("/{id}")
    public Mono<Void> deleteProduct(@PathVariable String id) {
        return service.deleteProduct(id);
    }
}

🔹 Explicação:

  • @GetMapping retorna todos os produtos (Flux).

  • @GetMapping(”/{id}”) retorna um único produto (Mono).

  • @PostMapping cria um novo produto (Mono).

  • @DeleteMapping(”/{id}”) deleta um produto (Mono).

6. Testando a API WebFlux

Após iniciar a aplicação (mvn spring-boot:run), podemos testar os endpoints:

📌 Criar um Produto (POST)

curl -X POST http://localhost:8080/products \
     -H "Content-Type: application/json" \
     -d '{"name":"Notebook","price":2500}'

📌 Listar Produtos (GET)

curl -X GET http://localhost:8080/products

📌 Buscar Produto por ID (GET)

curl -X GET http://localhost:8080/products/{id}

📌 Deletar Produto (DELETE)

curl -X DELETE http://localhost:8080/products/{id}

Conclusão

✅ Criamos uma API reativa com Spring WebFlux.

✅ Implementamos um CRUD reativo utilizando Mono e Flux.

✅ Utilizamos ReactiveCrudRepository para banco de dados reativo.

✅ Testamos a API via cURL.

Com essa base, você pode expandir sua aplicação adicionando validações, autenticação JWT, WebSockets e mensageria reativa. 🚀

9. Manipulação de Erros no WebFlux

A manipulação de erros é um aspecto essencial no desenvolvimento de APIs reativas com Spring WebFlux. Como a programação reativa é baseada em fluxos assíncronos, o tratamento de erros deve ser feito de forma adequada para evitar falhas inesperadas e garantir uma experiência estável para os usuários.

Neste artigo, veremos estratégias eficazes para lidar com erros no Spring WebFlux, utilizando os métodos do Projeto Reactor (onErrorResume, onErrorReturn, doOnError, etc.) e a personalização do tratamento de erros com @ControllerAdvice.

1. Como Funciona o Tratamento de Erros no WebFlux?

No Spring WebFlux, os erros são manipulados de forma reativa, ou seja, os fluxos de dados podem capturar e processar exceções sem interromper o fluxo global da aplicação.

O Projeto Reactor fornece diversos métodos para lidar com erros, como:

🔹 onErrorResume() – Substituir o erro por outro fluxo

✅ Captura um erro e retorna um valor alternativo ou um novo fluxo reativo.

🔹 onErrorReturn() – Retornar um valor fixo em caso de erro

✅ Quando ocorre um erro, retorna um valor padrão.

🔹 doOnError() – Executar uma ação ao detectar um erro

✅ Apenas loga ou executa uma ação, sem modificar o fluxo.

🔹 onErrorMap() – Converter um erro para outra exceção personalizada

✅ Permite mapear um erro genérico para uma exceção específica.

2. Tratamento de Erros no Mono e Flux

📌 1. Usando onErrorResume() para Substituir um Erro

O método onErrorResume() captura uma exceção e retorna um novo fluxo com um valor alternativo.

Mono<String> example = Mono.error(new RuntimeException("Erro!"))
    .onErrorResume(e -> Mono.just("Valor alternativo"));
    
example.subscribe(System.out::println);

Saída:

Valor alternativo

🔹 Explicação:

  • O erro “Erro!” é capturado e, em vez de lançar uma exceção, retorna “Valor alternativo”.

📌 2. Usando onErrorReturn() para Retornar um Valor Padrão

Caso um erro ocorra, o método onErrorReturn() retorna um valor fixo.

Flux<Integer> numbers = Flux.range(1, 5)
    .map(n -> {
        if (n == 3) throw new RuntimeException("Erro!");
        return n;
    })
    .onErrorReturn(-1);

numbers.subscribe(System.out::println);

Saída:

1
2
-1

🔹 Explicação:

  • Quando n == 3, ocorre uma exceção.

  • O fluxo é interrompido e retorna -1 como valor padrão.

📌 3. Usando doOnError() para Logar Erros sem Modificar o Fluxo

O doOnError() permite executar ações quando um erro ocorre, sem capturá-lo.

Mono<String> example = Mono.error(new RuntimeException("Falha no processo"))
    .doOnError(e -> System.out.println("Erro detectado: " + e.getMessage()));

example.subscribe(System.out::println, Throwable::printStackTrace);

🔹 Explicação:

  • O erro não é tratado nem substituído, apenas logado.

📌 4. Usando onErrorMap() para Mapear um Erro para Outra Exceção

Podemos transformar exceções genéricas em exceções personalizadas.

Mono<String> example = Mono.error(new IllegalArgumentException("Entrada inválida"))
    .onErrorMap(e -> new CustomException("Erro personalizado: " + e.getMessage()));

example.subscribe(System.out::println, Throwable::printStackTrace);

🔹 Explicação:

  • Qualquer erro original é convertido em uma CustomException personalizada.

3. Tratamento Global de Erros com @ControllerAdvice

Em APIs REST, é uma boa prática usar um tratador global de exceções para capturar erros e personalizar as respostas.

📌 Criando um Handler Global de Erros

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Mono<Map<String, String>> handleRuntimeException(RuntimeException ex) {
        Map<String, String> error = new HashMap<>();
        error.put("erro", ex.getMessage());
        return Mono.just(error);
    }

    @ExceptionHandler(CustomException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public Mono<Map<String, String>> handleCustomException(CustomException ex) {
        Map<String, String> error = new HashMap<>();
        error.put("erro", ex.getMessage());
        return Mono.just(error);
    }
}

🔹 Explicação:

  • @RestControllerAdvice intercepta todas as exceções da aplicação.

  • @ExceptionHandler mapeia exceções específicas para códigos de status HTTP adequados.

Agora, quando um erro ocorre, ele é tratado de forma padronizada.

4. Tratamento de Erros no Controller WebFlux

Agora, vamos aplicar o tratamento de erros no Controller da API.

📌 Criando um Controller com Tratamento de Erros

@RestController
@RequestMapping("/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService service;

    @GetMapping("/{id}")
    public Mono<ResponseEntity<Product>> getProductById(@PathVariable String id) {
        return service.getProductById(id)
                .map(ResponseEntity::ok)
                .onErrorResume(e -> Mono.just(ResponseEntity.notFound().build()));
    }

    @PostMapping
    public Mono<ResponseEntity<Product>> createProduct(@RequestBody Product product) {
        return service.saveProduct(product)
                .map(savedProduct -> ResponseEntity.status(HttpStatus.CREATED).body(savedProduct))
                .onErrorResume(e -> Mono.just(ResponseEntity.badRequest().build()));
    }
}

🔹 Explicação:

  • onErrorResume() retorna 404 Not Found se o produto não for encontrado.

  • ResponseEntity.badRequest() é usado para indicar erros de requisição inválida.

5. Testando o Tratamento de Erros

Após rodar a aplicação (mvn spring-boot:run), podemos testar os cenários de erro.

📌 Buscar Produto por ID inexistente

curl -X GET http://localhost:8080/products/999

🔹 Resposta Esperada (404 Not Found)

{
    "erro": "Produto não encontrado"
}

📌 Criar Produto com Dados Inválidos

curl -X POST http://localhost:8080/products \\
-H "Content-Type: application/json" \\
-d '{}'

🔹 Resposta Esperada (400 Bad Request)

{
    "erro": "Dados inválidos"
}

Conclusão

onErrorResume() → Substitui o erro por outro fluxo.

onErrorReturn() → Retorna um valor fixo em caso de erro.

doOnError() → Loga o erro sem tratá-lo.

onErrorMap() → Mapeia um erro para outra exceção.

@ControllerAdvice → Permite tratamento global de erros em APIs REST.

Erros podem ser tratados diretamente no Controller do WebFlux.

Com essas estratégias, garantimos que nossa API reativa seja robusta, confiável e tenha um tratamento de erros eficiente! 🚀

10. Uso do WebClient para Consumo de APIs Reativas

O WebClient é o cliente HTTP reativo do Spring WebFlux, projetado para substituir o tradicional RestTemplate. Ele permite realizar chamadas assíncronas para APIs REST e consumir serviços externos de forma reativa e não bloqueante.

Nesta seção, aprenderemos a configurar e utilizar o WebClient, incluindo métodos de requisição, manipulação de respostas, tratamento de erros e autenticação.

1. O Que é o WebClient?

O WebClient é uma API flexível e eficiente para realizar requisições HTTP assíncronas no Spring WebFlux.

Não bloqueante → Usa Mono e Flux para consumir APIs de forma assíncrona.

Suporte a backpressure → Controla o fluxo de dados recebidos.

Requisições encadeadas → Permite composição funcional de chamadas HTTP.

Autenticação integrada → Suporte a OAuth2, Basic Auth e JWT.

Ele pode ser usado para consumir APIs externas, integrar microservices e até substituir o RestTemplate em aplicações síncronas.

2. Configurando o WebClient

O WebClient pode ser configurado de três formas:

📌 1. Criando uma Instância Simples

Podemos criar uma instância padrão do WebClient de forma direta:

WebClient webClient = WebClient.create("https://api.exemplo.com");

Isso cria um cliente que faz chamadas para https://api.exemplo.com.

📌 2. Criando uma Instância Configurada com WebClient.Builder

Podemos personalizar o WebClient com configurações adicionais, como cabeçalhos globais e timeouts.

@Bean
public WebClient webClient(WebClient.Builder builder) {
    return builder
            .baseUrl("https://api.exemplo.com")
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .build();
}

Essa abordagem permite injeção do WebClient como dependência em outros componentes.

📌 3. Criando um WebClient Configurado Manualmente

WebClient webClient = WebClient.builder()
        .baseUrl("https://api.exemplo.com")
        .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer token_aqui")
        .build();

Aqui, adicionamos um cabeçalho de autenticação para todas as requisições.

3. Realizando Requisições HTTP com WebClient

O WebClient suporta os métodos HTTP GET, POST, PUT, DELETE e PATCH.

📌 1. Fazendo uma Requisição GET

Mono<String> response = WebClient.create()
        .get()
        .uri("https://api.exemplo.com/dados")
        .retrieve()
        .bodyToMono(String.class);

response.subscribe(System.out::println);        

🔹 Explicação:

  • .get().uri(“URL”) → Define a URL da requisição.

  • .retrieve() → Executa a requisição e obtém a resposta.

  • .bodyToMono(String.class) → Converte a resposta para Mono<String>.

  • .subscribe(System.out::println) → Processa o resultado de forma assíncrona.

📌 2. Fazendo uma Requisição POST

Mono<String> response = WebClient.create()
        .post()
        .uri("https://api.exemplo.com/produtos")
        .contentType(MediaType.APPLICATION_JSON)
        .bodyValue(new Product("Notebook", 2500.0))
        .retrieve()
        .bodyToMono(String.class);

response.subscribe(System.out::println);

🔹 Explicação:

  • .post() → Define que a requisição é POST.

  • .bodyValue(new Product(…)) → Envia um objeto no corpo da requisição.

📌 3. Fazendo uma Requisição PUT

Mono<String> response = WebClient.create()
        .put()
        .uri("https://api.exemplo.com/produtos/1")
        .contentType(MediaType.APPLICATION_JSON)
        .bodyValue(new Product("Notebook", 2999.0))
        .retrieve()
        .bodyToMono(String.class);

response.subscribe(System.out::println);

🔹 Explicação:

  • .put() → Atualiza um recurso existente.

📌 4. Fazendo uma Requisição DELETE

Mono<Void> response = WebClient.create()
        .delete()
        .uri("https://api.exemplo.com/produtos/1")
        .retrieve()
        .bodyToMono(Void.class);

response.subscribe();

🔹 Explicação:

  • .delete() → Remove um recurso.

  • .bodyToMono(Void.class) → Indica que a resposta não tem corpo.

4. Tratamento de Erros no WebClient

O WebClient oferece várias formas de lidar com erros em requisições HTTP.

📌 1. Capturando Erros com onStatus()

Mono<String> response = WebClient.create()
        .get()
        .uri("https://api.exemplo.com/produtos")
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError, clientResponse ->
                Mono.error(new RuntimeException("Erro do Cliente: " + clientResponse.statusCode())))
        .onStatus(HttpStatus::is5xxServerError, clientResponse ->
                Mono.error(new RuntimeException("Erro do Servidor: " + clientResponse.statusCode())))
        .bodyToMono(String.class);

response.subscribe(System.out::println, Throwable::printStackTrace);

🔹 Explicação:

  • .onStatus(HttpStatus::is4xxClientError, …) → Trata erros 4xx (cliente).

  • .onStatus(HttpStatus::is5xxServerError, …) → Trata erros 5xx (servidor).

📌 2. Tratamento Global de Erros com exchangeToMono()

Mono<String> response = WebClient.create()
        .get()
        .uri("https://api.exemplo.com/produtos")
        .exchangeToMono(clientResponse -> {
            if (clientResponse.statusCode().is2xxSuccessful()) {
                return clientResponse.bodyToMono(String.class);
            } else {
                return Mono.error(new RuntimeException("Erro: " + clientResponse.statusCode()));
            }
        });

response.subscribe(System.out::println, Throwable::printStackTrace);

🔹 Explicação:

  • .exchangeToMono() permite controle mais detalhado da resposta.

5. Autenticação com WebClient

📌 1. Autenticação Basic Auth

WebClient webClient = WebClient.builder()
        .baseUrl("https://api.exemplo.com")
        .defaultHeaders(headers -> headers.setBasicAuth("user", "password"))
        .build();

🔹 Explicação:

  • .setBasicAuth(“user”, “password”) → Envia as credenciais no cabeçalho.

📌 2. Autenticação com Token Bearer (JWT)

WebClient webClient = WebClient.builder()
        .baseUrl("https://api.exemplo.com")
        .defaultHeaders(headers -> headers.setBearerAuth("seu_token_aqui"))
        .build();

🔹 Explicação:

  • .setBearerAuth(“seu_token”) → Envia o token JWT no cabeçalho da requisição.

Conclusão

WebClient é a alternativa reativa ao RestTemplate.

✅ Suporta requisições GET, POST, PUT e DELETE de forma assíncrona.

✅ Permite tratamento de erros com onStatus() e exchangeToMono().

✅ Suporta autenticação Basic e JWT.

Com o WebClient, podemos integrar microservices, consumir APIs externas e processar dados de forma eficiente e escalável! 🚀

11. Banco de Dados Reativo com Spring Data R2DBC

O Spring Data R2DBC (Reactive Relational Database Connectivity) é a solução reativa para conectar bancos de dados relacionais no Spring WebFlux. Diferente do JDBC, que trabalha de forma bloqueante, o R2DBC permite consultas assíncronas e não bloqueantes, otimizando a performance de aplicações que precisam lidar com grandes volumes de requisições simultâneas.

Neste artigo, vamos aprender a configurar e usar o R2DBC no Spring WebFlux, criando um CRUD reativo com um banco de dados PostgreSQL.

1. O Que é o Spring Data R2DBC?

O R2DBC foi criado para permitir que bancos de dados relacionais funcionem de maneira reativa e assíncrona. Com ele, podemos executar consultas sem bloquear threads, garantindo maior eficiência e escalabilidade.

🔹 Por que usar R2DBC em vez de JDBC?

🔹 Modelo de Execução

JDBC (Tradicional): Bloqueante (1 thread por requisição).

R2DBC (Reativo): Não bloqueante (event loop processa várias conexões simultaneamente).

🔹 Concorrência

JDBC: Limitada pelo número de conexões simultâneas disponíveis no pool.

R2DBC: Alta escalabilidade, permitindo maior número de conexões com menos threads.

🔹 Uso de Recursos

JDBC: Pode consumir muitas threads, causando alto uso de CPU e memória.

R2DBC: Melhor aproveitamento de CPU e memória, otimizando a performance em sistemas concorrentes.

🔹 Melhor Para

JDBC: Aplicações síncronas tradicionais, como sistemas internos e monolíticos.

R2DBC: APIs de alta concorrência, microservices e aplicações reativas escaláveis.

Se a aplicação for síncrona, o JDBC ainda pode ser a melhor escolha. Mas para aplicações que precisam de escalabilidade e alto desempenho, Spring WebFlux + R2DBC é a solução ideal. 🚀

2. Configurando o Projeto com Spring Data R2DBC

Antes de começar, precisamos adicionar as dependências no nosso projeto Spring Boot + WebFlux.

📌 Adicionando as Dependências no pom.xml

<dependencies>
    <!-- Spring WebFlux -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <!-- Spring Data R2DBC -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-r2dbc</artifactId>
    </dependency>

    <!-- Driver R2DBC para PostgreSQL -->
    <dependency>
        <groupId>io.r2dbc</groupId>
        <artifactId>r2dbc-postgresql</artifactId>
    </dependency>

    <!-- Lombok (Para reduzir código boilerplate) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

🔹 Explicação:

  • spring-boot-starter-webflux → Ativa o WebFlux.

  • spring-boot-starter-data-r2dbc → Ativa o suporte ao R2DBC.

  • r2dbc-postgresql → Driver reativo para PostgreSQL.

  • lombok → Facilita a criação de entidades.

Se estiver usando Gradle, adicione as dependências ao build.gradle:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
    implementation 'io.r2dbc:r2dbc-postgresql'
    implementation 'org.projectlombok:lombok'
}

3. Configurando o Banco de Dados no application.properties

Agora, configuramos a conexão com o banco PostgreSQL.

📌 Edite o application.properties

spring.r2dbc.url=r2dbc:postgresql://localhost:5432/mydatabase
spring.r2dbc.username=admin
spring.r2dbc.password=admin
spring.r2dbc.pool.enabled=true

🔹 Explicação:

  • spring.r2dbc.url → URL de conexão no formato r2dbc:postgresql://:/.

  • spring.r2dbc.username → Nome de usuário do banco.

  • spring.r2dbc.password → Senha do banco.

  • spring.r2dbc.pool.enabled=true → Ativa o pool de conexões reativas.

Caso esteja usando outro banco, como MySQL ou MariaDB, troque o driver (r2dbc-mysql ou r2dbc-mariadb).

4. Criando o Modelo de Dados (Entity)

Vamos criar uma entidade Product para armazenar informações de produtos.

📌 Criação da classe Product.java no pacote model

package com.example.webflux.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Table("products") // Define a tabela do banco
public class Product {
    
    @Id
    private Long id;
    private String name;
    private double price;
}

🔹 Explicação:

  • @Table(“products”) → Define a tabela no banco.

  • @Id → Define a chave primária.

5. Criando o Repositório Reativo (Repository)

No Spring Data R2DBC, usamos ReactiveCrudRepository para operações CRUD não bloqueantes.

📌 Criação da interface ProductRepository.java no pacote repository

package com.example.webflux.repository;

import com.example.webflux.model.Product;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends ReactiveCrudRepository<Product, Long> {
}

🔹 O ReactiveCrudRepository fornece métodos prontos para CRUD reativo (save, findById, deleteById, etc.).

6. Criando o Serviço Reativo (Service)

A camada de serviço gerencia a lógica de negócios da aplicação.

📌 Criação da classe ProductService.java no pacote service

package com.example.webflux.service;

import com.example.webflux.model.Product;
import com.example.webflux.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
@RequiredArgsConstructor
public class ProductService {

    private final ProductRepository repository;

    public Flux<Product> getAllProducts() {
        return repository.findAll();
    }

    public Mono<Product> getProductById(Long id) {
        return repository.findById(id);
    }

    public Mono<Product> saveProduct(Product product) {
        return repository.save(product);
    }

    public Mono<Void> deleteProduct(Long id) {
        return repository.deleteById(id);
    }
}

7. Criando o Controller Reativo (API REST)

📌 Criação da classe ProductController.java no pacote controller

package com.example.webflux.controller;

import com.example.webflux.model.Product;
import com.example.webflux.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService service;

    @GetMapping
    public Flux<Product> getAllProducts() {
        return service.getAllProducts();
    }

    @GetMapping("/{id}")
    public Mono<Product> getProductById(@PathVariable Long id) {
        return service.getProductById(id);
    }

    @PostMapping
    public Mono<Product> createProduct(@RequestBody Product product) {
        return service.saveProduct(product);
    }

    @DeleteMapping("/{id}")
    public Mono<Void> deleteProduct(@PathVariable Long id) {
        return service.deleteProduct(id);
    }
}

Conclusão

R2DBC é a alternativa reativa ao JDBC.

✅ Suporta PostgreSQL, MySQL e outros bancos relacionais.

✅ Utiliza ReactiveCrudRepository para operações não bloqueantes.

✅ Permite criar APIs escaláveis e de alto desempenho.

Com essa abordagem, sua API estará pronta para microservices e arquiteturas reativas! 🚀

12. Testando Aplicações Reativas no Spring WebFlux

Testar aplicações reativas no Spring WebFlux exige uma abordagem diferente em relação a aplicações síncronas. Como tudo funciona de forma assíncrona e não bloqueante, precisamos utilizar ferramentas adequadas para validar fluxos de dados reativos corretamente.

Nesta seção, exploraremos como testar Controllers, Services e Repositories em aplicações WebFlux usando o WebTestClient, StepVerifier e Mockito.

1. Estratégias para Testes Reativos

No Spring WebFlux, utilizamos as seguintes estratégias para testes:

Testes de Unidade → Testa métodos individuais do Service e Repository, usando Mockito e StepVerifier.

Testes de Integração → Testa a aplicação completa, incluindo Controllers, usando WebTestClient.

Testes End-to-End (E2E) → Simula interações reais com APIs, verificando requisição e resposta.

Esses testes garantem que os fluxos reativos sejam consumidos corretamente, evitando erros e gargalos de desempenho.

2. Configuração das Dependências para Testes

Antes de começarmos, precisamos garantir que temos as dependências necessárias para testes em nosso projeto Spring Boot WebFlux.

📌 Adicione as dependências no pom.xml (para Maven):

<dependencies>
    <!-- Spring Boot Starter Test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- Reactor Test (Para testar Mono e Flux) -->
    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

🔹 Explicação:

  • spring-boot-starter-test → Inclui suporte ao JUnit 5 e Mockito.

  • reactor-test → Permite testar Mono e Flux corretamente.

Se estiver usando Gradle, adicione no build.gradle:

dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'io.projectreactor:reactor-test'
}

3. Testando Services e Repositories com StepVerifier

Os testes unitários verificam se os serviços e repositórios funcionam corretamente de forma reativa.

📌 Exemplo de Teste para ProductService

@SpringBootTest
class ProductServiceTest {

    @MockBean
    private ProductRepository productRepository;

    @Autowired
    private ProductService productService;

    @Test
    void testGetAllProducts() {
        Flux<Product> products = Flux.just(
                new Product(1L, "Notebook", 2500.0),
                new Product(2L, "Mouse", 50.0)
        );

        Mockito.when(productRepository.findAll()).thenReturn(products);

        StepVerifier.create(productService.getAllProducts())
                .expectNext(new Product(1L, "Notebook", 2500.0))
                .expectNext(new Product(2L, "Mouse", 50.0))
                .verifyComplete();
    }

    @Test
    void testGetProductById_NotFound() {
        Mockito.when(productRepository.findById(99L)).thenReturn(Mono.empty());

        StepVerifier.create(productService.getProductById(99L))
                .expectNextCount(0)
                .verifyComplete();
    }
}

🔹 Explicação:

  • Mockamos o ProductRepository para simular retornos de banco.

  • Usamos StepVerifier para validar os fluxos de dados (Flux e Mono).

  • expectNext() verifica se os objetos retornados são os esperados.

  • verifyComplete() confirma que o fluxo finalizou sem erros.

4. Testando Controllers com WebTestClient

O WebTestClient é a ferramenta recomendada para testes de integração com APIs reativas.

📌 Exemplo de Teste para ProductController

@WebFluxTest(ProductController.class)
class ProductControllerTest {

    @Autowired
    private WebTestClient webTestClient;

    @MockBean
    private ProductService productService;

    @Test
    void testGetAllProducts() {
        Flux<Product> products = Flux.just(
                new Product(1L, "Notebook", 2500.0),
                new Product(2L, "Mouse", 50.0)
        );

        Mockito.when(productService.getAllProducts()).thenReturn(products);

        webTestClient.get().uri("/products")
                .exchange()
                .expectStatus().isOk()
                .expectBodyList(Product.class)
                .hasSize(2)
                .contains(new Product(1L, "Notebook", 2500.0));
    }

    @Test
    void testGetProductById_NotFound() {
        Mockito.when(productService.getProductById(99L)).thenReturn(Mono.empty());

        webTestClient.get().uri("/products/99")
                .exchange()
                .expectStatus().isNotFound();
    }
}

🔹 Explicação:

  • @WebFluxTest(ProductController.class) → Indica que vamos testar o ProductController.

  • @MockBean para ProductService → Simula a lógica de negócio.

  • webTestClient.get().uri(“/products”) → Faz uma requisição GET.

  • expectStatus().isOk() → Valida o status HTTP 200 OK.

  • expectBodyList(Product.class).hasSize(2) → Confirma que a resposta contém 2 produtos.

  • expectStatus().isNotFound() → Valida que 404 é retornado para um produto inexistente.

5. Testando Requisições POST e DELETE

📌 Teste para Criar um Produto (POST)

@Test
void testCreateProduct() {
    Product product = new Product(null, "Teclado", 100.0);
    Product savedProduct = new Product(3L, "Teclado", 100.0);

    Mockito.when(productService.saveProduct(product)).thenReturn(Mono.just(savedProduct));

    webTestClient.post().uri("/products")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(product)
            .exchange()
            .expectStatus().isCreated()
            .expectBody(Product.class)
            .isEqualTo(savedProduct);
}

🔹 Explicação:

  • Criamos um produto e simulamos o retorno esperado.

  • post().uri(“/products”) → Faz a requisição POST.

  • bodyValue(product) → Envia o corpo da requisição.

  • expectStatus().isCreated() → Valida se retorna 201 Created.

📌 Teste para Deletar um Produto (DELETE)

@Test
void testDeleteProduct() {
    Mockito.when(productService.deleteProduct(1L)).thenReturn(Mono.empty());

    webTestClient.delete().uri("/products/1")
            .exchange()
            .expectStatus().isNoContent();
}

🔹 Explicação:

  • delete().uri(“/products/1”) → Envia requisição DELETE.

  • expectStatus().isNoContent() → Confirma retorno 204 No Content.

Conclusão

WebTestClient é a melhor opção para testes de APIs reativas.

StepVerifier permite testar fluxos de Mono e Flux de forma reativa.

Mockito facilita a simulação de repositórios e serviços.

Testes garantem que a API WebFlux é confiável e escalável.

Com essa abordagem, podemos testar nossa aplicação WebFlux de forma eficiente e robusta! 🚀

13. Desempenho e Escalabilidade do WebFlux

O Spring WebFlux foi projetado para oferecer alta escalabilidade e baixo consumo de recursos, tornando-se uma excelente escolha para aplicações que precisam lidar com grandes volumes de requisições simultâneas.

Neste artigo, exploraremos os fatores que influenciam o desempenho do WebFlux, como modelo de execução, gerenciamento de threads, backpressure e uso eficiente do banco de dados, além de comparações com o Spring MVC.

1. Como o Spring WebFlux Melhora o Desempenho?

O Spring WebFlux utiliza um modelo assíncrono e não bloqueante, diferente do Spring MVC, que segue um modelo baseado em threads bloqueantes.

🔹 Comparação entre Spring MVC e WebFlux:

🔹 Modelo de Execução

Spring MVC (Bloqueante): Cada requisição ocupa uma thread exclusiva até a resposta ser gerada.

Spring WebFlux (Reativo): Usa um event-loop, permitindo que uma única thread processe múltiplas requisições.

🔹 Concorrência

Spring MVC: Limitada pelo número máximo de threads disponíveis no servidor.

Spring WebFlux: Altamente escalável, suportando milhares de conexões simultâneas com menos threads.

🔹 Uso de CPU

Spring MVC: Alto consumo de CPU, pois usa muitas threads bloqueadas esperando respostas.

Spring WebFlux: Mais eficiente, reduzindo o uso de CPU por evitar bloqueios e trocas de contexto.

🔹 Melhor Para

Spring MVC: Ideal para aplicações tradicionais e monolíticas, onde o modelo síncrono é suficiente.

Spring WebFlux: Melhor escolha para microservices, APIs escaláveis e aplicações de alto desempenho.

🔹 No WebFlux, uma única thread pode lidar com várias requisições ao mesmo tempo, enquanto no Spring MVC, cada requisição ocupa uma thread do servidor até ser finalizada.

Isso reduz o consumo de memória e CPU, tornando o WebFlux mais escalável. 🚀

2. Modelo de Execução do WebFlux: Event Loop

O WebFlux é baseado no modelo de Event Loop, semelhante ao Node.js, permitindo um número menor de threads para gerenciar múltiplas conexões simultâneas.

🔹 Como funciona o modelo de Event Loop no WebFlux?

1️⃣ A requisição chega ao servidor.

2️⃣ É processada de forma assíncrona por um event loop.

3️⃣ A thread é liberada enquanto aguarda resposta do banco ou API externa.

4️⃣ Quando a resposta chega, a thread continua o processamento.

Esse modelo reduz trocas de contexto, pois evita a criação excessiva de threads, o que melhora o desempenho.

3. Gerenciamento de Threads no WebFlux

O WebFlux usa o Netty como servidor padrão, que implementa um modelo altamente eficiente de threads.

🔹 Número de Threads no WebFlux:

  • O Event Loop utiliza 2 × número de núcleos da CPU como thread pool.

  • Em média, 8 a 16 threads podem gerenciar milhares de conexões simultâneas.

🔹 Número de Threads no Spring MVC:

  • Cada requisição consome uma thread dedicada.

  • O servidor precisa de centenas ou milhares de threads para suportar alta concorrência.

📌 Exemplo de Configuração de Threads no WebFlux:

Podemos configurar manualmente o Scheduler do WebFlux para otimizar o desempenho:

Scheduler scheduler = Schedulers.newBoundedElastic(10, 100, "custom-scheduler");

Flux.range(1, 10)
    .publishOn(scheduler)
    .map(i -> "Item: " + i)
    .subscribe(System.out::println);

🔹 Explicação:

  • newBoundedElastic(10, 100, “custom-scheduler”) → Define 10 threads principais, podendo crescer até 100 threads se necessário.

4. Backpressure: Controle de Fluxo no WebFlux

Backpressure é o mecanismo que impede sobrecarga de requisições, garantindo que o sistema não seja sobrecarregado por um fluxo excessivo de dados.

🔹 Exemplo de Uso de Backpressure com Flux:

Flux.range(1, 1000)
    .onBackpressureDrop()
    .subscribe(System.out::println);

🔹 Explicação:

  • onBackpressureDrop() → Descarta elementos que o consumidor não consegue processar rapidamente.

Outras opções incluem:

  • onBackpressureBuffer() → Armazena os itens até que possam ser processados.

  • onBackpressureLatest() → Mantém apenas os valores mais recentes.

5. Uso Eficiente do Banco de Dados no WebFlux

O banco de dados pode ser um gargalo em aplicações WebFlux. Para garantir máxima performance, devemos usar bancos de dados reativos como R2DBC (PostgreSQL, MySQL) ou MongoDB Reativo.

🔹 Exemplo de Uso do Spring Data R2DBC:

@Repository
public interface ProductRepository extends ReactiveCrudRepository<Product, String> {
}

🔹 Modelo de Conexão

JDBC (Bloqueante): A execução da consulta aguarda a resposta, bloqueando a thread.

R2DBC (Reativo): Executa consultas de forma assíncrona, sem bloquear a thread.

🔹 Concorrência

JDBC: Baixa concorrência, pois cada requisição consome uma thread até a finalização da consulta.

R2DBC: Alta concorrência, permitindo a execução de múltiplas consultas simultaneamente sem bloqueios.

🔹 Melhor Para

JDBC: Ideal para aplicações tradicionais, onde a sincronização entre chamadas não é um problema.

R2DBC: Melhor escolha para microservices escaláveis e sistemas que exigem alta performance e baixa latência. Para aplicações altamente escaláveis, evite JDBC e prefira R2DBC! 🚀

6. Comparação de Desempenho: Spring WebFlux vs. Spring MVC

🔹 Testes de Benchmark mostram que Spring WebFlux pode processar até 3x mais requisições simultâneas do que o Spring MVC.

🔹 Requisições por Segundo

Spring MVC: Processa aproximadamente 2.000 requisições por segundo.

Spring WebFlux: Suporta até 6.000 requisições por segundo, devido ao modelo não bloqueante.

🔹 Uso de Memória (RAM)

Spring MVC: Consome mais memória, pois cada requisição mantém uma thread exclusiva.

Spring WebFlux: Otimiza o uso de RAM, pois uma única thread pode gerenciar várias requisições.

🔹 Uso de CPU

Spring MVC: Alto consumo de CPU, devido à necessidade de criar e gerenciar múltiplas threads.

Spring WebFlux: Mais eficiente, pois reduz trocas de contexto e maximiza o uso da CPU.

🔹 Escalabilidade

Spring MVC: Limitada pelo número de threads disponíveis, podendo gerar gargalos em alta concorrência.

Spring WebFlux: Altamente escalável, baseado no modelo de event-loop, permitindo melhor aproveitamento de recursos.

Em cenários de alta concorrência, o WebFlux oferece melhor desempenho e menor consumo de recursos.

7. Quando Usar WebFlux?

📌 O WebFlux é a Melhor Escolha Quando:

✅ O sistema precisa lidar com milhares de requisições simultâneas.

✅ Há chamadas para APIs externas e queremos evitar bloqueios de I/O.

✅ A aplicação usa bancos de dados reativos (R2DBC, MongoDB Reativo, Redis Reativo).

✅ Precisamos de baixo consumo de memória e CPU.

📌 O WebFlux NÃO é Necessário Quando:

❌ A aplicação é monolítica e tem baixa concorrência.

❌ A maioria das operações é síncrona e baseada em JDBC.

❌ O time não tem experiência com programação reativa (a curva de aprendizado pode ser alta).

Conclusão

Spring WebFlux melhora a performance e escalabilidade ao eliminar bloqueios de threads.

Event Loop reduz o consumo de memória e CPU, permitindo um maior número de conexões simultâneas.

Backpressure controla o fluxo de dados, evitando sobrecarga.

R2DBC melhora a performance ao eliminar bloqueios do banco de dados.

Em aplicações de alto desempenho, WebFlux pode processar até 3x mais requisições que Spring MVC.

Se o seu sistema precisa de alta escalabilidade e eficiência, o Spring WebFlux é a escolha certa! 🚀

14. Casos de Uso e Aplicações Reais do Spring WebFlux

O Spring WebFlux é amplamente utilizado em sistemas que exigem alta escalabilidade, baixa latência e grande volume de conexões simultâneas. Seu modelo reativo e não bloqueante o torna ideal para aplicações modernas que lidam com streams de dados, microservices, IoT e APIs de alto desempenho.

Neste artigo, exploraremos casos de uso reais, exemplos práticos e empresas que adotam o WebFlux para melhorar a eficiência e a escalabilidade de seus sistemas.

1. Quando Usar o Spring WebFlux?

O Spring WebFlux se destaca em cenários onde a escalabilidade é um fator crítico.

Ótimo para:

  • Sistemas com altas taxas de requisições simultâneas.

  • APIs reativas e não bloqueantes, que dependem de chamadas externas.

  • Microservices e arquitetura baseada em eventos.

  • Aplicações que lidam com streams de dados e WebSockets.

  • Integração com bancos de dados reativos como MongoDB e PostgreSQL via R2DBC.

Não recomendado para:

  • Aplicações tradicionais e monolíticas, onde o Spring MVC já é suficiente.

  • Projetos que utilizam JDBC, pois ele é bloqueante e pode anular os benefícios do WebFlux.

  • Times que não possuem experiência com programação reativa (curva de aprendizado pode ser um desafio).

2. Casos de Uso do Spring WebFlux

📌 1. APIs de Alto Desempenho e Escaláveis

O Spring WebFlux é ideal para construir APIs REST escaláveis que atendem milhares de requisições simultâneas sem degradar o desempenho.

🔹 Exemplo:

  • Empresas que oferecem APIs públicas para desenvolvedores, como Stripe, PayPal e Twilio, precisam garantir alta disponibilidade e baixa latência.

  • Plataformas de e-commerce que recebem grande volume de acessos simultâneos em promoções, como Black Friday.

🔹 Exemplo de um Controller WebFlux para API de Produtos:

@RestController
@RequestMapping("/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService productService;

    @GetMapping
    public Flux<Product> getAllProducts() {
        return productService.getAllProducts();
    }

    @GetMapping("/{id}")
    public Mono<ResponseEntity<Product>> getProductById(@PathVariable String id) {
        return productService.getProductById(id)
                .map(ResponseEntity::ok)
                .defaultIfEmpty(ResponseEntity.notFound().build());
    }
}

🔹 Vantagens:

  • Usa Flux para listas grandes e Mono para objetos únicos.

  • Escala melhor que Spring MVC em aplicações de alto tráfego.

📌 2. Microservices e Comunicação Assíncrona

A arquitetura de microservices geralmente envolve múltiplos serviços se comunicando. Com o Spring WebFlux, podemos usar o WebClient para consumir APIs externas de forma reativa e eficiente.

🔹 Exemplo: Comunicação entre microservices com WebClient

WebClient webClient = WebClient.create("http://user-service");

public Mono<User> getUserById(String userId) {
    return webClient.get()
            .uri("/users/{id}", userId)
            .retrieve()
            .bodyToMono(User.class);
}

🔹 Vantagens:

  • Comunicação assíncrona e não bloqueante.

  • Reduz tempo de resposta e melhora a escalabilidade.

🔹 Empresas que usam:

  • Netflix e Uber usam microservices para lidar com milhões de requisições por segundo.

  • Amazon e Alibaba adotam arquitetura baseada em eventos para sistemas distribuídos.

📌 3. Streaming de Dados e Processamento em Tempo Real

Empresas que lidam com streaming de dados contínuos usam WebFlux para processar e transmitir informações em tempo real.

🔹 Casos de uso:

  • Monitoramento de IoT: Dispositivos conectados enviam dados de sensores em tempo real.

  • Aplicações financeiras: Bolsas de valores transmitem dados de ações em tempo real.

  • Redes sociais e chats: Aplicações como Facebook, Twitter e WhatsApp processam eventos assíncronos.

🔹 Exemplo: Streaming de Dados com WebFlux e Server-Sent Events (SSE)

@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamData() {
    return Flux.interval(Duration.ofSeconds(1))
               .map(i -> "Evento #" + i);
}

🔹 Vantagens:

  • Baixa latência e suporte a grandes volumes de dados.

  • WebSockets e Server-Sent Events para comunicação contínua.

📌 4. Integração com Bancos de Dados Reativos

O WebFlux funciona melhor quando combinado com bancos de dados reativos como MongoDB e R2DBC (PostgreSQL, MySQL, etc.).

🔹 Exemplo: CRUD com Spring Data R2DBC (PostgreSQL)

@Repository
public interface ProductRepository extends ReactiveCrudRepository<Product, String> {
}

🔹 Vantagens:

  • Consultas assíncronas e não bloqueantes.

  • Melhor aproveitamento de conexões em grandes volumes de dados.

🔹 Empresas que usam:

  • Google e Microsoft usam bancos de dados reativos para Big Data e AI.

  • Netflix usa MongoDB Reativo para armazenar logs e eventos de usuários.

📌 5. Aplicações para IoT (Internet das Coisas)

Dispositivos IoT precisam enviar dados continuamente para um servidor central. O Spring WebFlux é ideal para processar e armazenar esses eventos sem sobrecarregar o sistema.

🔹 Exemplo: Captura de Dados de Sensores IoT

@PostMapping("/sensor-data")
public Mono<ResponseEntity<Void>> receiveSensorData(@RequestBody SensorData data) {
    return sensorService.processData(data)
            .then(Mono.just(ResponseEntity.ok().build()));
}

🔹 Vantagens:

  • Processa milhares de eventos por segundo.

  • Reduz o consumo de recursos em dispositivos IoT.

🔹 Empresas que usam:

  • Tesla e Bosch usam WebFlux para processar dados de veículos conectados.

  • IBM e Cisco adotam arquiteturas reativas para gerenciar redes IoT.

Conclusão

Spring WebFlux é ideal para sistemas de alta escalabilidade e baixa latência.

✅ Empresas como Netflix, Uber e Tesla usam WebFlux para microservices e streaming de dados.

APIs REST, IoT, chat em tempo real e sistemas financeiros são os principais casos de uso.

✅ Bancos reativos como R2DBC e MongoDB Reativo garantem melhor desempenho em bancos relacionais e NoSQL.

Se sua aplicação precisa ser reativa, escalável e altamente eficiente, o Spring WebFlux é a escolha certa! 🚀

15. Conclusão

O Spring WebFlux representa uma grande evolução no desenvolvimento de aplicações web ao introduzir um modelo reativo, assíncrono e não bloqueante. Ele se destaca por proporcionar melhor escalabilidade e desempenho, tornando-se a escolha ideal para sistemas que lidam com altas taxas de requisições simultâneas, microservices e streaming de dados.

📌 Recapitulando os Principais Pontos

✅ Por que usar o Spring WebFlux?

  • Permite alta escalabilidade ao usar menos threads.

  • Reduz o consumo de CPU e memória, melhorando a performance.

  • Funciona bem com bancos de dados reativos (R2DBC, MongoDB, Redis).

  • Ideal para microservices, APIs REST escaláveis e comunicação assíncrona.

  • Suporte nativo a WebSockets, SSE e streaming de dados.

✅ Principais Tecnologias Integradas

O WebFlux se destaca quando combinado com:

  • Projeto Reactor → Mono e Flux para manipulação reativa de dados.

  • WebClient → Consumo de APIs assíncronas e comunicação entre microservices.

  • R2DBC e MongoDB Reativo → Bancos de dados não bloqueantes para máxima eficiência.

  • Spring Security Reativo → Autenticação e autorização seguras em ambientes reativos.

✅ Casos de Uso Ideais

Spring WebFlux é amplamente utilizado em: 🚀 APIs de alto desempenho → APIs REST que precisam processar milhares de requisições simultâneas. 📡 Microservices escaláveis → Comunicação eficiente e assíncrona entre serviços. 💬 Chats e WebSockets → Aplicações em tempo real, como WhatsApp e Facebook Messenger. 📊 Streaming de dados e IoT → Monitoramento contínuo de sensores e dispositivos inteligentes. 🏦 Sistemas financeiros → Bolsa de valores e processamento de transações em tempo real.

📢 Quando NÃO Usar o Spring WebFlux?

Embora o WebFlux seja poderoso, ele não é a melhor escolha para todos os cenários.

❌ Se sua aplicação é monolítica e não precisa de alta concorrência, o Spring MVC ainda é uma excelente opção.

❌ Se você usa JDBC tradicional, o WebFlux pode não trazer benefícios reais, pois as consultas ainda serão bloqueantes.

❌ Aplicações com baixa carga de requisições podem não se beneficiar do modelo reativo.

🚀 Próximos Passos: Como Adotar o Spring WebFlux?

1️⃣ Migrar aplicações Spring MVC para WebFlux:

  • Avalie os requisitos de escalabilidade antes de migrar.

  • Troque o RestTemplate pelo WebClient para chamadas HTTP assíncronas.

  • Use R2DBC em vez de JDBC para garantir uma abordagem 100% reativa.

2️⃣ Aprender Programação Reativa:

  • Estude Mono e Flux para manipulação eficiente de fluxos de dados.

  • Explore operadores do Projeto Reactor como map(), flatMap(), merge(), zip().

3️⃣ Testar WebFlux com WebTestClient e StepVerifier:

  • Utilize WebTestClient para validar endpoints reativos.

  • Use StepVerifier para testar fluxos de dados no Service e Repository.

🔚 Conclusão Final

O Spring WebFlux é uma excelente escolha para aplicações modernas que exigem desempenho, escalabilidade e processamento assíncrono. Ele permite a construção de APIs reativas, microservices eficientes e sistemas baseados em eventos, garantindo menor consumo de recursos e alta disponibilidade.

Se sua aplicação precisa suportar milhares de requisições simultâneas e processar dados de forma eficiente, o Spring WebFlux é a tecnologia ideal para levar sua aplicação para o próximo nível! 🚀🔥

Gostou deste guia? Compartilhe e comece a desenvolver aplicações reativas agora mesmo! 🚀😃