Pular para conteúdo

Aula 18 - Processamento Assíncrono com Spring Events 📬

Objetivo Pedagógico

Objetivo: Implementação do padrão Event-Driven Architecture em aplicações Spring Boot através de ApplicationEventPublisher, @EventListener e processamento assíncrono com @Async.


📑 1. Fundamentos Teóricos & Análise Técnica

Em aplicações empresariais robustas, o princípio do Desacoplamento de Domínio dita que um serviço principal (como o fechamento de um pedido comercial) não deve ter conhecimento nem dependência direta de ações secundárias (como disparo de notificações por e-mail, atualização de pontos de fidelidade ou auditoria fiscal).

O ecossistema Spring fornece um subsistema nativo para orquestrar essa comunicação através do padrão Observer / Publisher-Subscriber: 1. Publicador (ApplicationEventPublisher): O serviço de negócio injeta o publicador e emite um evento de domínio (publisher.publishEvent(new OrderCreatedEvent(order))). 2. Ouvinte (@EventListener): Classes especializadas assinam o evento e reagem à sua ocorrência de forma isolada. 3. Execução Não-Bloqueante (@Async): Com a anotação @Async, o método do ouvinte é executado em uma thread separada gerenciada por um pool de execução (ThreadPoolTaskExecutor), liberando a thread da requisição HTTP imediatamente sem reter o usuário.

📐 Arquitetura Conceitual & Diagrama de Fluxo

sequenceDiagram
    autonumber
    actor Client as Cliente HTTP
    participant Service as OrderService (Thread Principal)
    participant Publisher as ApplicationEventPublisher
    participant Listener as EmailNotificationListener (Thread Pool Async)

    Client->>Service: POST /orders (Criar Pedido)
    Service->>Service: Salva Pedido no Banco de Dados
    Service->>Publisher: publishEvent(OrderCreatedEvent)
    Service-->>Client: 201 Created (Resposta Imediata!)
    Publisher->>Listener: Dispara Evento em Background (@Async)
    Note over Listener: Envio de E-mail Executado sem Atrasar a API!

🔍 Pilares e Diretrizes Técnicas

Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Desacoplamento de Domínio: O serviço de pedidos não importa nem depende de classes de infraestrutura de e-mail. - Pool de Threads Parametrizado: Configuração de corePoolSize, maxPoolSize e queueCapacity no TaskExecutor. - Eventos Transacionais (@TransactionalEventListener): Garantia de que o evento seja disparado apenas após a confirmação do commit no banco. - Resiliência a Falhas de E-mail: Se o envio de e-mail falhar, a transação da compra não é revertida indevidamente.


🛠️ 2. Implementação Prática em Java, Spring Boot e Event-Driven Architecture

Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:

// OrderCreatedListener.java (Ouvinte Assíncrono de Eventos de Domínio)
package com.empresa.core.events;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

public record OrderCreatedEvent(String orderId, String customerEmail, double amount) {}

@Component
public class OrderEventsListener {

    @Async("asyncTaskExecutor")
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("[Thread Async: " + Thread.currentThread().getName() + 
                           "] Enviando e-mail de confirmação para: " + event.customerEmail());
        // Simula comunicação com provedor externo de e-mail
        try {
            Thread.sleep(3000);
        } catch (InterruptedException ignored) {}
        System.out.println("[Thread Async] E-mail enviado com sucesso para pedido: " + event.orderId());
    }
}

💡 Análise Passo a Passo do Código

  1. Fase AFTER_COMMIT: @TransactionalEventListener garante que o e-mail só seja enviado se o pedido for salvo com sucesso na base de dados.
  2. Execução @Async: O método é descarregado para o pool de threads asyncTaskExecutor, não travando a resposta HTTP.
  3. Java Record Imutável: Uso de record para criar objetos de eventos leves, imutáveis e livres de código boilerplate.

🎯 3. Próximos Passos & Sequência Didática