🚨 Most Spring Boot applications still do this wrong. Let's use some event and virtual threads 🚀 👉 A user registers. 1. You save the user. 2. Then you send an email. All Inside the same request. 💣 Bad idea. Why? Because now your API depends on: • SMTP latency • network delays • external failures Your simple signup endpoint suddenly becomes slow and fragile. 💡 A better pattern is Domain Events. Instead of sending the email directly, publish an event: eventPublisher.publishEvent( new NewUserRegisteredEvent(user.id(), user.name(), user.email()) ); Now your API just registers the user and everything else becomes a side effect. 🚀 We will handle the email asynchronously: @Async("eventTaskExecutor") @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void onNewUserRegistered(NewUserRegisteredEvent event) { // send activation email } Notice the key detail: 👉 AFTER_COMMIT The email only runs after the transaction succeeds. No ghost emails. No inconsistent state. Now comes the interesting part. 💡 Instead of using a traditional thread pool… Use Java 21 Virtual Threads. @Bean public TaskExecutor eventTaskExecutor() { return new VirtualThreadTaskExecutor("event-vt-"); } This gives you: ⚡ Massive concurrency ⚡ Lightweight threads ⚡ No reactive complexity ⚡ Clean imperative code Suddenly you can handle thousands of async tasks like: • emails • notifications • integrations • audit logs • background jobs Without blocking requests. Without complicated frameworks. Java 21 quietly changed backend scalability. And most developers still haven’t realized it. Are you already using Virtual Threads in production or still relying on traditional thread pools? #Java #SpringBoot #Java21 #VirtualThreads #BackendDevelopment #SoftwareArchitecture
Interesting. Just wondering what happens when an email fails to send, presumably because the email address is incorrect? Is there a rollback to the first commit, or will the user simply not receive the email and not know why?
Using Domain Events decouples the API from external dependencies, improving overall resilience. How do you handle event handling failures, such as retry mechanisms or dead letter queues?
This comes from the Java language itself, not from Spring Boot.
It's great to see how Java continues to modernize. The ease with which this can be done without using a RabbitMQ-style solution is incredible. Thank you for sharing your knowledge; I will definitely use it in future projects!
Using AFTER_COMMIT is a lifesaver to avoid those annoying ghost emails. Moving this to Virtual Threads makes it so cheap and performant. We are slowly migrating our async tasks to VTs right now. It really changes the game for backend scalability. Thanks for sharing!
But what if sending email is critical? In this case decoupling user creation from notification breaks a unit of work. What if email address is no more valid, etc? Sometimes event model will not meet business requirement.