springspring-modulith

Where to place events in a Spring Modulith application?


When using Spring Modulith with events, is it better practice placing the events of each module within each module?

- com.example
  |- product
     |- application
     |- domain
     |- events
        |- Product.java
        |- ProductEvent.java
        |- package-info.java
     |- spi
     |- web
  |- order
     ...

or better in a shared module?

- com.example
  |- product
     |- application
     |- domain
     |- spi
     |- web
     ...
  |- shared
     |- events
        |- product
           |- Product.java
           |- ProductEvent.java          
        |- order
           |- Order.java
           |- OrderEvent.java   
  ...

In my opinion, if we place the events in the shared module, our modules will be total decoupled. If one day we needed to extract a module as an independent service (prob this will never happen), we will not have any other module referring to this module.


Solution

  • Events belong to the modules that publish them. Primarily, they're the outcome of state transitions on aggregates. That's why you want to keep them closely to those. ProductEvent is not an event, really. ProductRegistered or OrderCanceled are.

    In my opinion, if we place the Events in the shared module, our modules will be total decoupled.

    There's no such thing as total decoupling. All you achieve is that all modules will be coupled to the shared one, introducing the downside of reducing cohesion of all modules at the same time. shared will become a collection of unrelated things.

    The term “shared” not bearing any business relevance, already indicating that's a bad candidate for a module. Not saying that there's no shared code at all or a module containing shared code must never exist. I'm just saying that it's unlikely to be the starting point of a general recommendation for any structural question, as the module is the primary anchor in any modulithic application.

    Events can also implement interfaces, too. If the question arose from the need to resolve a cyclic dependency (the primary reason, folks reflexively move code into a shared module), be reminded that a module can also expose an interface, declare an event listener to that and have another module's event implementing that interface.