An in-process publication can occur before or after a transaction commits; listener timing must match the state contract.
Spring Modulith event ordering: decide which state the listener may observe
Publish a fact after the guarded decision
The inventory slice emits ReceiptReserved only after its in-memory reservation succeeds. In a database-backed command, publish while the business transaction is active so transactional listeners and a configured registry can associate the event with that decision. A regular synchronous listener may run before commit and must not assume other connections can read the new row.
Choose the observation point
An after-commit listener can see committed state but its later failure cannot roll back that state change. For an external message, persist delivery intent with the business write and relay it separately. Transaction-event timing and outbox atomicity explain those two boundaries.
Keep per-key order explicit
Two reservations for one receipt may publish events in a different order from listener completion when work is asynchronous. Carry receipt ID and version, then reject an older version at the consumer. The current module fixture has no asynchronous listener, database transaction or broker, so this remains a design contract.
Boundary sketch
record ReceiptReserved(String receiptId, int units,
int remaining, long version) {}Cost and verification
Version checks add a keyed read or compare-and-set at the consumer. Asynchronous listeners add queues and backpressure decisions inside the process. This design remains unexecuted in the source kit.
Common Mistakes
- Do not assume an after-commit failure rolls back the source row.
- Do not infer listener completion order from event publication order.
- Do not call the local fixture a durable event system.
Read next
Spring Modulith events: publish a domain fact after a valid state change, Spring Modulith event registry recovery: separate publication, listener and broker states, Spring transaction events: run a listener after commit without claiming durability, Spring consumer deduplication: commit the event ID with the mutation.
