Internal Application Events and Listeners
Decouple modules using domain events published and consumed within the application context.
Why Decouple Modules with Events?
In a modular monolith, an Order module often needs to trigger work in other modules: send a confirmation email, update inventory, award loyalty points. The naive approach is to have OrderService call EmailService, InventoryService, and LoyaltyService directly.
- This creates tight coupling — Order depends on every downstream module.
- Adding a new reaction means editing
OrderServiceagain. - Modules can no longer evolve or be tested independently.
Spring's internal application events invert this: the Order module simply publishes a domain event, and interested modules listen for it. The publisher never knows who is listening.
The Domain Event Record
An internal event is just a plain object — in Spring Boot 4 (Java 21+) a record is the idiomatic choice. It should be immutable and carry only the data listeners need.
- Name it in the past tense (
OrderCompleted) — it describes something that already happened. - Keep it inside the owning module's package.
- No Spring annotations are required on the event itself.
package com.shop.order;
import java.math.BigDecimal;
import java.util.UUID;
public record OrderCompleted(
UUID orderId,
String customerEmail,
BigDecimal total) {
}All lessons in this course
- Application Modules and Boundary Verification
- Internal Application Events and Listeners
- Transactional Event Publication and Outbox
- Module Integration Testing and Scenarios