Skip to content

Add grails-transactional-events v8 guide#509

Open
jamesfredley wants to merge 2 commits into
masterfrom
add-grails-transactional-events-v8
Open

Add grails-transactional-events v8 guide#509
jamesfredley wants to merge 2 commits into
masterfrom
add-grails-transactional-events-v8

Conversation

@jamesfredley
Copy link
Copy Markdown
Contributor

Adds a new Grails 8 guide:

Cross-Domain Logic in Grails with Transactional Spring Events

The pattern - publish a POGO from a @Transactional service via ApplicationEventPublisher, consume it from any number of @TransactionalEventListener(phase = AFTER_COMMIT) beans - is the cleanest answer to "trigger business logic across several unrelated domains after a domain object changes state, but only if the change actually commits". It works out-of-the-box with GORM because GORM rides on Spring's PlatformTransactionManager: the AFTER_COMMIT synchronisation callback fires exactly when the GORM transaction commits to the database, and is silently skipped on rollback. There is no Grails-specific bridge.

This gap is not covered by any current Grails 8 guide. The existing grails-events guide (v3 / v4) walks through the Grails-native EventBus / @Subscriber model, which fires at the Hibernate-event level rather than the Spring-transaction level - the wrong tool for cross-domain "only on durable commit" side-effects.

Sample-app flavour

The guide is sample-app flavour. The matching upstream repo lives at grails-guides/grails-transactional-events on the grails8 branch (initialised separately).

Chapter arc (17 chapters)

# Chapter What it covers
1 gettingStarted Framing, target stack (Grails 8 / Spring Boot 4 / JDK 21)
2 whatYouWillBuild Artifacts list
3 requirements JDK 21, ~30 minutes
4 howto Clone-and-run shortcut
5 theProblem Why the naive approaches (stuff-it-in-OrderService, GORM afterInsert, plain ApplicationEvent) fail
6-7 createApp / download Vanilla Grails 8 starter
8 domainModel Customer, Order, AuditLog
9 eventClass OrderPlacedEvent POGO under src/main/groovy/example/events/
10 publishingEvents OrderService.placeOrder() with @Transactional + ApplicationEventPublisher
11 transactionalListener First @TransactionalEventListener(AFTER_COMMIT)
12 transactionPhases Reference table for all four TransactionPhase values
13 multipleListeners Fanning out - second listener, decoupled
14 asyncDispatch @Async + @TransactionalEventListener + @EnableAsync
15 vsGormEvents Side-by-side with GORM lifecycle callbacks and @Subscriber / @Listener
16 integrationTest Spock @Integration spec proving rollback semantics
17 helpWithGrails Common include

Local verification

./gradlew validateGuides -PvalidationMode=both
# [validateGuides] mode=both: 94 guide(s) parsed, 0 SKIP-warned, 0 errors

./gradlew renderGuide_grails_transactional_events_8
# Rendering document file gettingStarted.adoc
# ...
# Rendering document file helpWithGrails.adoc
# [ant:echo] Built user manual at build/dist/guides/grails-transactional-events/8/index.html

All 17 chapters render with every include::../snippets/...[] directive resolved (10 source files cited in the rendered HTML).

Files

  • conf/guides.yml - new entry in alphabetical position (between grails-test-security and grails-tvmlapp), category Grails Async, full v8-style toc with sub-keys mirroring the recent v8 batch (grails-rest-library, grails-spock-test-tour, grails-vite-spa).
  • guides/grails-transactional-events/v8/guide/*.adoc - 17 chapters.
  • guides/grails-transactional-events/v8/snippets/ - 10 source files (3 domains, 4 services, 1 BootStrap, 1 POGO event, 1 integration spec).

Related

  • Upstream sample-app repo: grails-guides/grails-transactional-events on grails8 branch (initialised separately by the PMC).
  • Closest structural template: grails-rest-library v8 and grails-spock-test-tour v8 (both recent sample-app-flavour Grails 8 guides).

Cross-domain business logic in Grails using Spring application events
with @TransactionalEventListener(AFTER_COMMIT). The pattern works
out-of-the-box with GORM because GORM rides on Spring's
PlatformTransactionManager: AFTER_COMMIT listeners fire when (and only
when) the publisher's transaction commits, and are silently skipped on
rollback.

Sample-app-flavour Grails 8 guide that walks through:

- An OrderService that publishes an OrderPlacedEvent POGO from a
  @transactional method via ApplicationEventPublisher.
- Three independent listeners (CustomerLifetimeValueListener,
  AuditListener, NotificationListener) wired purely by Spring
  @TransactionalEventListener annotations on grails-app/services/
  beans.
- An @async + @TransactionalEventListener composition for off-thread
  dispatch, with @EnableAsync wiring on the Application class.
- A reference table of all four TransactionPhase values
  (BEFORE_COMMIT, AFTER_COMMIT, AFTER_ROLLBACK, AFTER_COMPLETION).
- A side-by-side contrast with GORM domain-class lifecycle callbacks
  and Grails' @subscriber / @Listener annotations.
- A Spock @Integration spec that opens its own committed and
  rolled-back transactions to prove the AFTER_COMMIT contract.

Registry entry added to conf/guides.yml with the standard v8 sample-app
shape (sourcePath, tags, sampleRef pointing at
grails-guides/grails-transactional-events grails8 branch, full toc).

./gradlew validateGuides -PvalidationMode=both passes (94 guides, 0
errors). ./gradlew renderGuide_grails_transactional_events_8 renders
all 17 chapters with every include:: directive resolved.

Assisted-by: claude-code:claude-opus-4-7
Copilot AI review requested due to automatic review settings May 27, 2026 13:45
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new sample-app-style Grails 8 guide covering Spring transactional application events for after-commit cross-domain side effects in GORM-backed applications.

Changes:

  • Registers grails-transactional-events v8 in conf/guides.yml.
  • Adds guide chapters explaining the transactional event pattern, listener phases, async dispatch, and integration testing.
  • Adds sample snippets for domains, services/listeners, bootstrap data, event POGO, and integration spec.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
conf/guides.yml Registers the new guide, metadata, tags, sample repo, and TOC.
guides/grails-transactional-events/v8/guide/gettingStarted.adoc Introduces the guide’s transactional events pattern.
guides/grails-transactional-events/v8/guide/whatYouWillBuild.adoc Summarizes the sample application artifacts.
guides/grails-transactional-events/v8/guide/requirements.adoc Lists guide prerequisites.
guides/grails-transactional-events/v8/guide/howto.adoc Provides clone-and-run instructions.
guides/grails-transactional-events/v8/guide/createApp.adoc Describes starter app creation and dependencies.
guides/grails-transactional-events/v8/guide/download.adoc Shows start.grails.org download commands.
guides/grails-transactional-events/v8/guide/domainModel.adoc Documents the sample domain model.
guides/grails-transactional-events/v8/guide/eventClass.adoc Explains the POGO event class.
guides/grails-transactional-events/v8/guide/publishingEvents.adoc Covers publishing events from OrderService.
guides/grails-transactional-events/v8/guide/transactionalListener.adoc Explains the first after-commit listener.
guides/grails-transactional-events/v8/guide/transactionPhases.adoc Documents TransactionPhase options.
guides/grails-transactional-events/v8/guide/multipleListeners.adoc Adds the audit listener fan-out example.
guides/grails-transactional-events/v8/guide/asyncDispatch.adoc Explains @Async listener dispatch.
guides/grails-transactional-events/v8/guide/vsGormEvents.adoc Compares Spring transactional events with GORM/Grails events.
guides/grails-transactional-events/v8/guide/integrationTest.adoc Documents the integration test strategy.
guides/grails-transactional-events/v8/guide/helpWithGrails.adoc Includes the shared help chapter.
guides/grails-transactional-events/v8/snippets/grails-app/domain/example/Customer.groovy Adds the customer domain snippet.
guides/grails-transactional-events/v8/snippets/grails-app/domain/example/Order.groovy Adds the order domain snippet.
guides/grails-transactional-events/v8/snippets/grails-app/domain/example/AuditLog.groovy Adds the audit log domain snippet.
guides/grails-transactional-events/v8/snippets/src/main/groovy/example/events/OrderPlacedEvent.groovy Adds the immutable event payload snippet.
guides/grails-transactional-events/v8/snippets/grails-app/services/example/OrderService.groovy Adds the transactional publisher service snippet.
guides/grails-transactional-events/v8/snippets/grails-app/services/example/CustomerLifetimeValueListener.groovy Adds the customer lifetime value listener snippet.
guides/grails-transactional-events/v8/snippets/grails-app/services/example/AuditListener.groovy Adds the audit listener snippet.
guides/grails-transactional-events/v8/snippets/grails-app/services/example/NotificationListener.groovy Adds the async notification listener snippet.
guides/grails-transactional-events/v8/snippets/grails-app/init/example/BootStrap.groovy Adds sample bootstrap data.
guides/grails-transactional-events/v8/snippets/src/integration-test/groovy/example/OrderServiceIntegrationSpec.groovy Adds integration coverage for commit and rollback behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread guides/grails-transactional-events/v8/guide/transactionPhases.adoc Outdated
Comment thread guides/grails-transactional-events/v8/guide/transactionalListener.adoc Outdated
Comment thread guides/grails-transactional-events/v8/guide/whatYouWillBuild.adoc Outdated
Comment thread guides/grails-transactional-events/v8/guide/asyncDispatch.adoc Outdated
Four factual corrections to the guide narrative. No code-snippet
changes; the sample app at grails-guides/grails-transactional-events
is unaffected.

1. transactionPhases.adoc / transactionalListener.adoc: "publisher's
   GORM session is closed" overstates the Spring lifecycle. After-commit
   synchronizations run after commit but BEFORE
   cleanupAfterCompletion(), so resources may still be bound to the
   thread. Rephrased to: the original transaction has already
   concluded, and the listener body cannot piggy-back on a transaction
   that has finished - hence REQUIRES_NEW. Same fix applied to both
   chapters.

2. whatYouWillBuild.adoc: "Two Spock integration specs" mismatched the
   actual snippet, which is one @Integration spec
   (OrderServiceIntegrationSpec) with two test methods. Bullet rewritten
   to match.

3. asyncDispatch.adoc: SimpleAsyncUncaughtExceptionHandler logs at ERROR
   (verified from Spring source: logger.error(...)), not WARN. Replaced
   the inaccurate log-level reference and expanded the operational
   advice about installing an explicit handler.

./gradlew validateGuides -PvalidationMode=both still passes (94 guides,
0 errors). renderGuide_grails_transactional_events_8 re-renders all 17
chapters with zero warnings; the four corrected paragraphs appear in
the per-chapter and single-page HTML.

Assisted-by: claude-code:claude-opus-4-7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants