Skip to content

fix(domain-events): correct payload guidance — keep events serializable#12734

Open
bradystroud wants to merge 1 commit into
mainfrom
fix-domain-events-payload-guidance
Open

fix(domain-events): correct payload guidance — keep events serializable#12734
bradystroud wants to merge 1 commit into
mainfrom
fix-domain-events-payload-guidance

Conversation

@bradystroud
Copy link
Copy Markdown
Member

@bradystroud bradystroud commented May 16, 2026

  1. What triggered this change? (PBI link, Email Subject, conversation + reason, etc)

While auditing a project's internal rules compare against SSW Rules, I noticed this rule advises "the entire Entity or Aggregate can be included since the event's scope is confined to the current bounded context."

We learned the hard way that this guidance doesn't hold up once an Outbox (or any async/persisted event handler) is introduced. Events holding an aggregate root fail to deserialize with System.NotSupportedException: Deserialization of types without a parameterless constructor... is not supported, because aggregate roots typically have factory methods, private set / init properties, and EF Core navigation properties. Events get stuck in the outbox and never publish.

The same problem affects event sourcing, audit replay, and promotion to Integration Events — anything that requires JSON serialization.

  1. What was changed?

In public/uploads/rules/when-to-use-domain-and-integration-events/rule.mdx:

  • Removed the inaccurate sentence claiming the full aggregate can be passed in a domain event.
  • Added a new ## Event payloads — keep them serializable section with:
    • The three concrete reasons events need to serialize (Outbox, event sourcing, Integration Event promotion).
    • Bad/Good csharp examples — bad passes the aggregate, good passes flat primitives + IDs + enums-as-int.
    • A guidelines list: what to include, what to exclude, how to flatten value objects.
    • A short "Why not just pass the entity?" subsection — explains why this looks fine in-process but breaks the moment you bolt on anything async.

Bounded-context greybox kept; Integration Events section untouched; frontmatter unchanged (lastUpdated / lastUpdatedBy are system-managed).

Frontmatter validates clean via scripts/frontmatter-validator.

  1. I paired or mob programmed with:

🤖 Drafted with Claude Code

Replaces the claim that the entire entity/aggregate can be included in a
domain event with concrete guidance that payloads should be primitives
and IDs only. Adds bad/good code examples and rationale tied to the
Transactional Outbox Pattern, event sourcing, and promotion to
Integration Events — all of which require JSON serialization that
aggregate roots are not built for.
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Preview PR Changes

View updated pages in edit mode:

Branch: fix-domain-events-payload-guidance

@github-actions github-actions Bot added the Age: 🥚 - New About 2 hours old label May 16, 2026
@github-actions
Copy link
Copy Markdown
Contributor

👋 Hi @bradystroud,

This PR is currently in Draft mode. To help it get reviewed and merged:

  1. ✍️ Update PR title & add a description - Explain what changes you've made and why
  2. Mark as ready for review - Click "Ready for review" when you're done
  3. 👥 Add reviewers - Assign people to review your changes
  4. 📣 Notify reviewers - Message or call them to let them know the PR is ready

Once you've completed these steps, your PR will be ready for approval!

This is an automated reminder to help reduce draft PRs without descriptions or reviewers.

@github-actions github-actions Bot added Age: 🐣 - Young About 4 hours old Age: 🐥 - Adolescent About 8 hours old Age: 🐤 - Mature About 16 hours old Age: 🐓 - Old About 32 hours old Age: 🍗 - Ancient About 64 hours old 🔥 Merge Debt This PR contains merge debt, see https://www.ssw.com.au/rules/merge-debt/ and removed Age: 🥚 - New About 2 hours old Age: 🐣 - Young About 4 hours old Age: 🐥 - Adolescent About 8 hours old Age: 🐤 - Mature About 16 hours old Age: 🐓 - Old About 32 hours old labels May 16, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Howzit @bradystroud,

This PR has been here a while.

Did you know you should avoid merge debt?

  1. Please action (e.g. get a review) and merge or close

Thanks!

@github-actions github-actions Bot added Age: 🦖 - Extinct About 128 hours old and removed Age: 🍗 - Ancient About 64 hours old labels May 19, 2026
@bradystroud bradystroud marked this pull request as ready for review May 31, 2026 07:08
@github-actions github-actions Bot added the Age: 🥚 - New About 2 hours old label May 31, 2026
@bradystroud bradystroud enabled auto-merge (squash) May 31, 2026 07:12
@github-actions github-actions Bot removed the Age: 🥚 - New About 2 hours old label May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Age: 🦖 - Extinct About 128 hours old 🔥 Merge Debt This PR contains merge debt, see https://www.ssw.com.au/rules/merge-debt/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant