📱 Implementando Eventos com Spring: Um Exemplo Prático 🚀



This content originally appeared on DEV Community and was authored by Uiratan Cavalcante

Introdução

Imagine que vocĂȘ estĂĄ desenvolvendo um sistema de streaming de mĂșsica. Sempre que um usuĂĄrio assina o serviço, queremos realizar vĂĄrias açÔes automaticamente, como enviar um e-mail de boas-vindas, conceder um perĂ­odo de teste gratuito e atualizar o status da assinatura.

Em vez de acoplar essas açÔes diretamente no fluxo de cadastro, podemos utilizar eventos para manter o cĂłdigo mais organizado, flexĂ­vel e desacoplado. Vamos ver como o Spring pode nos ajudar a fazer isso automaticamente! 😃

🎯 O Problema de Negócio

Ao cadastrar um novo assinante, precisamos executar diversas açÔes:

  • Enviar um e-mail de boas-vindas đŸ“©
  • Conceder um perĂ­odo de teste grĂĄtis 🎁
  • Atualizar o status do usuĂĄrio ✅

Uma abordagem ingĂȘnua seria simplesmente chamar esses mĂ©todos diretamente dentro do fluxo de cadastro, mas isso criaria forte acoplamento entre as açÔes. Queremos um sistema mais flexĂ­vel, onde possamos adicionar ou remover eventos sem modificar a lĂłgica principal.

🛠 Implementação com Eventos

Utilizaremos o Spring para gerenciar automaticamente os eventos e suas respectivas açÔes.

1⃣ Definição da Classe Assinatura

Criamos a classe Assinatura que representa um usuĂĄrio assinante:

public class Assinatura {
    private String usuario;

    public Assinatura(String usuario) {
        this.usuario = usuario;
    }

    public String getUsuario() {
        return usuario;
    }
}

2⃣ Definição da Interface do Evento

Primeiro, criamos uma interface que define o contrato para todos os eventos que devem ser executados apĂłs um novo cadastro:

public interface EventoNovaAssinatura {
    void processa(Assinatura assinatura);
}

Essa interface serĂĄ implementada por todas as classes que precisam reagir a uma nova assinatura.

3⃣ Implementação dos Eventos

Agora, implementamos as açÔes específicas que devem ocorrer ao criar uma nova assinatura.

đŸ“© Enviar e-mail de boas-vindas

@Service
public class EmailBoasVindas implements EventoNovaAssinatura {
    @Override
    public void processa(Assinatura assinatura) {
        System.out.println("[EMAIL] Bem-vindo, " + assinatura.getUsuario() + "! Sua assinatura foi ativada.");
    }
}

🎁 Conceder teste gratuito

@Service
public class TesteGratis implements EventoNovaAssinatura {
    @Override
    public void processa(Assinatura assinatura) {
        System.out.println("[TESTE GRÁTIS] Usuårio " + assinatura.getUsuario() + " recebeu um período de teste de 7 dias!");
    }
}

✅ Atualizar status da assinatura

@Service
public class AtualizaStatus implements EventoNovaAssinatura {
    @Override
    public void processa(Assinatura assinatura) {
        System.out.println("[STATUS] Assinatura de " + assinatura.getUsuario() + " ativada com sucesso!");
    }
}

4⃣ Classe que Orquestra os Eventos

Criamos um serviço que gerencia a execução dos eventos automaticamente:

@Service
public class EventosNovaAssinatura {
    @Autowired
    private Set<EventoNovaAssinatura> eventos;

    public void processa(Assinatura assinatura) {
        eventos.forEach(evento -> evento.processa(assinatura));
    }
}

Aqui, o Spring automaticamente injeta todos os beans que implementam EventoNovaAssinatura no Set<EventoNovaAssinatura> eventos. Isso significa que sempre que adicionarmos uma nova implementação, ela será registrada sem precisar modificar nada no código principal! 🎉

🎯 Como o Spring Injeta Automaticamente os Eventos?

O Spring escaneia automaticamente todas as classes anotadas com @Service que implementam a interface EventoNovaAssinatura. Ele injeta todas essas instùncias no Set<EventoNovaAssinatura> eventos, garantindo que todos os eventos sejam processados corretamente sem precisar de configuração manual.

Isso segue o princípio da inversão de controle e permite adicionar novos eventos sem modificar o código principal, tornando o sistema mais modular e fácil de manter. ✅

OpçÔes de Expansão com Eventos

O uso de eventos nos permite adicionar novas açÔes de forma simples. Algumas possíveis implementaçÔes que poderiam ser adicionadas ao nosso fluxo:

  • 📩 Integração com mensagerias: Publicar eventos no RabbitMQ ou Kafka para processamentos assĂ­ncronos.
  • 🔔 NotificaçÔes push: Enviar notificaçÔes para o celular do usuĂĄrio.
  • 💳 Cobrança automĂĄtica: Integrar com um sistema de pagamentos para ativar a cobrança recorrente.
  • 📊 Registro de mĂ©tricas: Enviar dados para um sistema de monitoramento, como o Prometheus.
  • 🛠 CustomizaçÔes avançadas: Criar um workflow que permita aos administradores ativar ou desativar eventos dinamicamente.

🛠 PadrĂ”es e PrincĂ­pios

🔄 Padrão Observer

No nosso exemplo, utilizamos o Padrão Observer, onde a classe EventosNovaAssinatura age como um sujeito que notifica um conjunto de observadores (EventoAssinaturaConfirmada). Esse padrão permite desacoplar a lógica de notificação do fluxo principal, garantindo que novas açÔes possam ser adicionadas sem modificar código existente.

Isso se alinha ao PrincĂ­pio Aberto-Fechado (OCP – Open/Closed Principle), pois podemos adicionar novos eventos sem modificar a implementação de EventosNovaAssinatura. Basta criar uma nova classe que implemente EventoAssinaturaConfirmada, e o Spring automaticamente a registrarĂĄ.

🔌 InversĂŁo de Controle e Injeção de DependĂȘncias

O Spring faz o gerenciamento automĂĄtico das dependĂȘncias ao injetar dinamicamente todas as implementaçÔes de EventoAssinaturaConfirmada dentro do Set<EventoAssinaturaConfirmada> eventoAssinaturaConfirmada. Isso elimina a necessidade de instanciar manualmente esses objetos, facilitando a escalabilidade e testabilidade do cĂłdigo.

Essa abordagem segue o PrincĂ­pio da InversĂŁo de DependĂȘncia (DIP – Dependency Inversion Principle), pois as classes de alto nĂ­vel (EventosNovaAssinatura) nĂŁo dependem diretamente das implementaçÔes concretas dos eventos, mas sim da abstração (EventoAssinaturaConfirmada).

🏗 PrincĂ­pio da Responsabilidade Única (SRP – Single Responsibility Principle)

Cada classe do nosso cĂłdigo tem uma Ășnica responsabilidade:

  • EventosNovaAssinatura: responsĂĄvel por processar assinaturas e notificar os eventos.
  • NotificacaoEmail: envia um e-mail confirmando a assinatura.
  • GerarRelatorio: atualiza o sistema com um novo relatĂłrio.
  • Assinatura: representa uma assinatura com status de confirmação.

Esse design modular melhora a organização do cĂłdigo e facilita a manutenção, pois cada classe foca em uma Ășnica tarefa.

📚 Conclusão

Com essa abordagem, conseguimos um sistema flexĂ­vel, modular e extensĂ­vel, permitindo adicionar novas notificaçÔes sem modificar cĂłdigo existente. O uso do PadrĂŁo Observer, combinado com a injeção de dependĂȘncias do Spring, nos ajuda a manter um cĂłdigo alinhado aos princĂ­pios do SOLID, melhorando a testabilidade, manutenção e escalabilidade da aplicação.

Se gostou desse conteĂșdo, compartilhe com outros desenvolvedores e ajude a disseminar boas prĂĄticas no desenvolvimento Java! 💡


This content originally appeared on DEV Community and was authored by Uiratan Cavalcante