spring-projects / spring-modulith

Modular applications with Spring Boot
https://spring.io/projects/spring-modulith
Apache License 2.0
715 stars 112 forks source link

Adding `spring-boot-starter-data-jpa` dependency stops `@ApplicationModuleListener` receiving events #477

Open timothysparg opened 5 months ago

timothysparg commented 5 months ago

the following snippet works until I add the spring-boot-starter-data-jpa dependency.

@EnableScheduling
@SpringBootApplication
public class DemoApplication {

  private ApplicationEventPublisher publisher;

    public DemoApplication(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @ApplicationModuleListener
    public void onCustomEvent(CustomEvent event) {
        System.out.println("Event received!");
    }

    @Scheduled(timeUnit = TimeUnit.SECONDS, fixedRate = 5)
    public void fireEvents() {
        System.out.println("firing event");
        publisher.publishEvent(new CustomEvent());
    }

    record CustomEvent() {}
}

Is there something that I am missing ?

timothysparg commented 5 months ago

when I add @Transactional then it works.

    @Transactional
    @Scheduled(timeUnit = TimeUnit.SECONDS, fixedRate = 5)
    public void fireEvents() {
        System.out.println("firing event");
        publisher.publishEvent(new CustomEvent());
    }

In hindsight when I look at the code snippets in the events reference page I can see the implication that all events need to be transacted, but maybe it should be more explict?

odrotbohm commented 5 months ago

I am inclined your original code won't work without the JPA starter either as @ApplicationModuleListener is a @TransactionalEventListener in standard Spring terms. Thus, without the scheduled method producing the event within some transaction, it won't actually get invoked anyway.

timothysparg commented 4 months ago

Hi Oliver,

Apologies for the delayed response, I was on holiday 🙂

I've created the a repo where I replicated the above. https://github.com/timothysparg/sm-477

If you run that app without any modifications it should fire and receive events. If you uncomment the spring-boot-starter-data-jpa dependency in the pom then it will never receive any events.

Arondc commented 4 months ago

@timothysparg I ran into the exact same issue. The issue is, that the method that produces publishes your events needs to be @Transactional. You've already mentioned that in your past comment.

As far as I understood it, as long as you don't add a database starter to your Modulith project, Modulith won't try to persist your events and thus no @Transactional is needed, because the event handling won't (can't?) be backed by an event publication registry.

As soon as a database starter is part of your project it will automatically connect to your datasource to keep your events in a table.

This may be an oversight in autoconfiguration and/or documentation.

timothysparg commented 4 months ago

As far as I understood it, as long as you don't add a database starter to your Modulith project, Modulith won't try to persist your events and thus no @Transactional is needed, because the event handling won't (can't?) be backed by an event publication registry.

I think you need to add an explicit modulith db starter for it to persist events to the database. Or at least that is the behaviour that I have seen.

This may be an oversight in autoconfiguration and/or documentation.

Broadly this is what I am trying to understand - if it is the expected behaviour I feel like it should be more explicit and if possible shouldn't just silently fail. There will be other people trying to get started who run into the same thing.

odrotbohm commented 4 months ago

Thanks, everyone, for chiming in here. I dug into the example and here are my findings.

I've filed spring-projects/spring-framework#32319 to fix the transactional event listener handling in case no transactional infrastructure is enabled. I am inclined to close this as working as designed.