DEV Community

Ledson Oliveira da Silva
Ledson Oliveira da Silva

Posted on

Desacoplando lógicas com PublishEvent + EventHandler no Spring Boot

Quando estamos desenvolvendo aplicações no Spring Boot, é comum nos depararmos com cenários em que uma única ação precisa disparar várias consequências.

Pense no caso de cadastro de usuário: além de salvar os dados no banco, talvez seja necessário enviar um e-mail de boas-vindas, registrar um log, ou até notificar outro sistema.

A solução mais direta seria implementar tudo dentro do próprio UserService. Mas logo surge o problema: o método que deveria apenas cadastrar o usuário acaba assumindo várias responsabilidades, ficando cada vez mais difícil de manter e evoluir.

👉 E aí surge a pergunta: como podemos desacoplar essas lógicas sem transformar nosso código em um monólito cheio de dependências internas ou partir imediatamente para soluções complexas como Kafka ou microsserviços?

É exatamente aí que entram os eventos no Spring Boot.
Com o uso de publishEvent e @EventListener, conseguimos criar uma comunicação interna desacoplada, onde o serviço principal apenas publica o que aconteceu, e outros componentes ficam responsáveis por reagir a isso de forma independente.

Neste artigo, vou mostrar na prática:

  • Como usar eventos no Spring Boot.
  • Vantagens dessa abordagem.
  • Boas práticas e armadilhas comuns.
  • Como esse padrão se conecta com arquiteturas maiores, como CQRS e event-driven.

📌 Como usar eventos no Spring Boot

O Spring Boot já oferece suporte nativo a eventos, e o fluxo básico é bem simples:

Definimos um evento (geralmente representando algo que aconteceu no domínio).

Publicamos esse evento em algum ponto do sistema.

Criamos handlers/listeners que irão reagir a ele.

Vamos ver isso passo a passo em um exemplo prático de cadastro de usuário.

1. Criando o evento

Podemos representar um evento como uma classe simples.
No nosso caso, vamos criar um UserCreatedEvent para indicar que um novo usuário foi registrado.

public record UserCreatedEvent(Long userId, String email) {}
Enter fullscreen mode Exit fullscreen mode

💡 Aqui usei um record para deixar o código mais conciso, mas você pode usar uma classe normal se preferir.

2. Publicando o evento

Dentro do UserService, assim que o usuário for cadastrado, vamos publicar o evento.

@Service
@RequiredArgsConstructor
public class UserService {

    private final ApplicationEventPublisher publisher;

    public void registerUser(String email) {
        // Simulação de cadastro
        User user = saveUser(email);

        // Publica o evento
        publisher.publishEvent(new UserCreatedEvent(user.getId(), user.getEmail()));
    }

    private User saveUser(String email) {
        // persistência fake só para exemplo
        return new User(1L, email);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Reagindo ao evento

Agora podemos criar quantos handlers quisermos para reagir ao evento.
O Spring Boot cuida da mágica para chamar cada um deles automaticamente.

@Component
public class SendWelcomeEmailHandler {

    @EventListener
    public void handle(UserCreatedEvent event) {
        System.out.println("Enviando e-mail para: " + event.email());
    }
}
Enter fullscreen mode Exit fullscreen mode
@Component
public class CreateLogHandler {

    @EventListener
    public void handle(UserCreatedEvent event) {
        System.out.println("Criando log para usuário: " + event.userId());
    }
}
Enter fullscreen mode Exit fullscreen mode

🔎 Note como o UserService não tem conhecimento de como o e-mail é enviado ou onde o log é registrado. Ele apenas publica o evento, e cada handler faz o seu trabalho de forma independente.

Assim, com poucas linhas de código, já conseguimos um fluxo desacoplado e extensível: se amanhã você quiser adicionar mais uma ação (como notificar outro sistema via API), basta criar um novo handler, sem tocar no código de cadastro.


🚀 Vantagens de usar eventos no Spring Boot

Adotar a publicação e tratamento de eventos no Spring Boot traz diversos benefícios para a arquitetura da aplicação, mesmo em projetos que ainda são monólitos.

🔹 1. Desacoplamento

O serviço principal (ex.: UserService) não precisa conhecer os detalhes de como cada ação complementar será executada.
Ele só dispara o evento — e os handlers cuidam do resto.
Isso reduz dependências diretas entre classes e facilita a manutenção.

🔹 2. Extensibilidade

Se amanhã surgir a necessidade de enviar um push notification ou integrar com outro sistema, não é necessário modificar o código do cadastro de usuário.
Basta criar um novo handler para reagir ao mesmo evento.

Ou seja, o sistema cresce de forma aberta para extensão e fechada para modificação (princípio OCP do SOLID).

🔹 3. Organização do código

Cada responsabilidade fica em seu próprio componente, evitando métodos gigantes que fazem “tudo ao mesmo tempo”.
Isso deixa o código mais legível e as classes mais coesas.

🔹 4. Reuso e testabilidade

Handlers podem ser testados isoladamente, sem precisar passar por todo o fluxo do serviço principal.
Além disso, um mesmo evento pode ser usado para disparar múltiplas ações em contextos diferentes.

🔹 5. Preparação para arquiteturas maiores

Mesmo em um monólito, trabalhar com eventos já cria uma mentalidade event-driven, tornando a transição para soluções com mensageria (Kafka, RabbitMQ) ou padrões como CQRS bem mais natural.


⚠️ Armadilhas e boas práticas

Apesar de trazerem muitas vantagens, os eventos no Spring Boot também exigem atenção para evitar problemas de manutenção e comportamento inesperado.

⚠️ 1. Eventos são síncronos por padrão

Quando você usa publishEvent, o fluxo espera todos os handlers terminarem antes de seguir.

Se algum handler for lento (ex.: envio de e-mail externo) ou lançar exceção, pode impactar diretamente o serviço principal.

✅ Boas práticas:

Use @Async nos handlers que podem rodar em paralelo, evitando travar o fluxo principal.

Para isso, basta habilitar @EnableAsync na sua aplicação.

⚠️ 2. Cuidados com transações

Se você publicar o evento dentro de uma transação, ele será disparado imediatamente — mesmo que a transação ainda não tenha sido confirmada.
Isso pode causar inconsistência (ex.: handler tenta acessar dados que ainda não foram persistidos).

✅ Boas práticas:

Use @TransactionalEventListener para garantir que o handler só seja executado após o commit da transação.

Exemplo:

@Component
public class CreateLogHandler {

    @TransactionalEventListener
    public void handle(UserCreatedEvent event) {
        System.out.println("Criando log após commit para usuário: " + event.userId());
    }
}
Enter fullscreen mode Exit fullscreen mode

⚠️ 3. Não transformar tudo em evento

É tentador usar eventos para “qualquer coisa”, mas isso pode deixar o sistema difícil de debugar e seguir o fluxo.

✅ Boas práticas:

Use eventos quando há múltiplos interessados em uma mesma ação.

Para lógicas simples e locais, prefira manter o código direto no serviço.

⚠️ 4. Tratamento de falhas

Por padrão, se um handler lançar exceção, ela pode se propagar e afetar o fluxo.
Isso pode ser crítico se o evento for parte de uma ação de negócio essencial.

✅ Boas práticas:

Isolar a lógica dos handlers com try/catch.

Para eventos assíncronos, configurar retries ou dead-letter (quando migrar para mensageria).

👉 Em resumo: eventos são poderosos, mas não são bala de prata. Usados com consciência, ajudam a manter a aplicação organizada e preparada para crescer.


🔗 Conexão com arquiteturas maiores

Trabalhar com publishEvent e @EventListener no Spring Boot pode parecer algo simples, mas na prática é um primeiro passo para arquiteturas mais robustas.

Esse padrão já introduz uma mentalidade event-driven, que facilita a migração futura para:

CQRS – separando comandos e queries.

Mensageria (Kafka, RabbitMQ) – distribuindo eventos entre microsserviços.

Escalabilidade – permitindo que novas funcionalidades sejam adicionadas sem acoplar ao core.

✅ Conclusão

Usar eventos no Spring Boot é uma forma prática de desacoplar responsabilidades, manter o código mais limpo e extensível, além de preparar o terreno para arquiteturas mais avançadas.

Mesmo em aplicações monolíticas, essa abordagem já traz ganhos reais de organização e manutenção.

👉 E você, já usou eventos no seu projeto com Spring Boot? Prefere manter tudo síncrono ou já partiu para mensageria com Kafka/RabbitMQ? Conta aí nos comentários!

Top comments (0)