Implementando Eventos com Spring: Um Exemplo Prático

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:

<span>public</span> <span>class</span> <span>Assinatura</span> <span>{</span>
<span>private</span> <span>String</span> <span>usuario</span><span>;</span>
<span>public</span> <span>Assinatura</span><span>(</span><span>String</span> <span>usuario</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>usuario</span> <span>=</span> <span>usuario</span><span>;</span>
<span>}</span>
<span>public</span> <span>String</span> <span>getUsuario</span><span>()</span> <span>{</span>
<span>return</span> <span>usuario</span><span>;</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>Assinatura</span> <span>{</span>
    <span>private</span> <span>String</span> <span>usuario</span><span>;</span>

    <span>public</span> <span>Assinatura</span><span>(</span><span>String</span> <span>usuario</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>usuario</span> <span>=</span> <span>usuario</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>String</span> <span>getUsuario</span><span>()</span> <span>{</span>
        <span>return</span> <span>usuario</span><span>;</span>
    <span>}</span>
<span>}</span>
public class Assinatura { private String usuario; public Assinatura(String usuario) { this.usuario = usuario; } public String getUsuario() { return usuario; } }

Enter fullscreen mode Exit fullscreen mode

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:

<span>public</span> <span>interface</span> <span>EventoNovaAssinatura</span> <span>{</span>
<span>void</span> <span>processa</span><span>(</span><span>Assinatura</span> <span>assinatura</span><span>);</span>
<span>}</span>
<span>public</span> <span>interface</span> <span>EventoNovaAssinatura</span> <span>{</span>
    <span>void</span> <span>processa</span><span>(</span><span>Assinatura</span> <span>assinatura</span><span>);</span>
<span>}</span>
public interface EventoNovaAssinatura { void processa(Assinatura assinatura); }

Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

Conceder teste gratuito

<span>@Service</span>
<span>public</span> <span>class</span> <span>TesteGratis</span> <span>implements</span> <span>EventoNovaAssinatura</span> <span>{</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>processa</span><span>(</span><span>Assinatura</span> <span>assinatura</span><span>)</span> <span>{</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"[TESTE GRÁTIS] Usuário "</span> <span>+</span> <span>assinatura</span><span>.</span><span>getUsuario</span><span>()</span> <span>+</span> <span>" recebeu um período de teste de 7 dias!"</span><span>);</span>
<span>}</span>
<span>}</span>
<span>@Service</span>
<span>public</span> <span>class</span> <span>TesteGratis</span> <span>implements</span> <span>EventoNovaAssinatura</span> <span>{</span>
    <span>@Override</span>
    <span>public</span> <span>void</span> <span>processa</span><span>(</span><span>Assinatura</span> <span>assinatura</span><span>)</span> <span>{</span>
        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"[TESTE GRÁTIS] Usuário "</span> <span>+</span> <span>assinatura</span><span>.</span><span>getUsuario</span><span>()</span> <span>+</span> <span>" recebeu um período de teste de 7 dias!"</span><span>);</span>
    <span>}</span>
<span>}</span>
@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!"); } }

Enter fullscreen mode Exit fullscreen mode

Atualizar status da assinatura

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

Enter fullscreen mode Exit fullscreen mode

4️⃣ Classe que Orquestra os Eventos

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

<span>@Service</span>
<span>public</span> <span>class</span> <span>EventosNovaAssinatura</span> <span>{</span>
<span>@Autowired</span>
<span>private</span> <span>Set</span><span><</span><span>EventoNovaAssinatura</span><span>></span> <span>eventos</span><span>;</span>
<span>public</span> <span>void</span> <span>processa</span><span>(</span><span>Assinatura</span> <span>assinatura</span><span>)</span> <span>{</span>
<span>eventos</span><span>.</span><span>forEach</span><span>(</span><span>evento</span> <span>-></span> <span>evento</span><span>.</span><span>processa</span><span>(</span><span>assinatura</span><span>));</span>
<span>}</span>
<span>}</span>
<span>@Service</span>
<span>public</span> <span>class</span> <span>EventosNovaAssinatura</span> <span>{</span>
    <span>@Autowired</span>
    <span>private</span> <span>Set</span><span><</span><span>EventoNovaAssinatura</span><span>></span> <span>eventos</span><span>;</span>

    <span>public</span> <span>void</span> <span>processa</span><span>(</span><span>Assinatura</span> <span>assinatura</span><span>)</span> <span>{</span>
        <span>eventos</span><span>.</span><span>forEach</span><span>(</span><span>evento</span> <span>-></span> <span>evento</span><span>.</span><span>processa</span><span>(</span><span>assinatura</span><span>));</span>
    <span>}</span>
<span>}</span>
@Service public class EventosNovaAssinatura { @Autowired private Set<EventoNovaAssinatura> eventos; public void processa(Assinatura assinatura) { eventos.forEach(evento -> evento.processa(assinatura)); } }

Enter fullscreen mode Exit fullscreen mode

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!

原文链接: Implementando Eventos com Spring: Um Exemplo Prático

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
The shortest way to do many things is to only one thing at a time.
做许多事情的捷径就是一次只做一件一件事
评论 抢沙发

请登录后发表评论

    暂无评论内容