Skip to content

feat(car-sharing): EventBus broadcast / fan-out on trip completion (Phase 3)#29

Draft
intech wants to merge 2 commits into
mainfrom
feat/car-sharing-eventbus-broadcast
Draft

feat(car-sharing): EventBus broadcast / fan-out on trip completion (Phase 3)#29
intech wants to merge 2 commits into
mainfrom
feat/car-sharing-eventbus-broadcast

Conversation

@intech

@intech intech commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

⚠️ BLOCKED — merge only AFTER @connectum/events 1.1.0 is published

This example uses EventBusOptions.publishes (framework PR #166), which ships in 1.1.0not yet on npm (it's in the open Version Packages PR connectum#165). The example pins ^1.0.0, which resolves to 1.1.0 once published; until then a plain pnpm install pulls 1.0.0 (no publishes) and the example fails to typecheck/run. Opened as a draft on purpose. Un-draft and merge after the events 1.1.0 release.

What

Phase 3 adds the third orthogonal interaction mechanism to car-sharing: a fire-and-forget EventBus broadcast / fan-out. When the Phase-2 Temporal saga settles a trip, TripCompleted is published once and consumed independently by 3 reactors (pricing/analytics, audit-log, notifications). EventBus is for broadcast only — orchestration stays Temporal's job.

Phase Mechanism Shape
1 ctx.call sync typed RPC
2 Temporal saga durable orchestration + compensation
3 EventBus broadcast fire-and-forget 1 → N independent reactors

How

  • proto/trips/v1/trip_events.protoTripCompleted + TripEventHandlers, topic trips.completed via the (connectum.events.v1.event).topic option.
  • publishes (PR #166) — the publisher resolves the declared topic from the proto option with no raw topic string (the clean replacement for the old hand-passed-topic workaround). Verified: no {topic} passed to any publish().
  • Fan-out = 3 separate reactor buses, each a distinct consumer group — forced by the per-bus duplicate-topic guard, and giving real-broker fan-out (distinct durable consumers). Reactors are idempotent by tripId.
  • Publish site = a new terminal saga activity publishTripCompleted in the worker. Failure semantics: a success-only try/catch tail outside the saga try/catch (reached only when SETTLED; its catch does not rethrow) — a failed broadcast can never roll back a settled, paid trip. Separate proxyActivities retry block for the publish.

Tests (dockerless)

One publish drives all 3 reactors over a shared MemoryAdapter (full TripCompleted shape asserted), the topic resolves to trips.completed from the proto option, topic isolation, and a guard test (broadcast failure leaves the trip SETTLED). buf generate/lint + typecheck exit 0; pnpm test 41/41 (all Phase-2 suites still green). docker-compose nats (JetStream) + 3 reactor processes on the saga profile are config-only.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MdeH7fExPmiRHRirGuvGk3

…hase 3)

Add the third orthogonal interaction mechanism: a fire-and-forget broadcast.
When the Temporal saga settles a trip, publish TripCompleted ONCE; three
independent reactors (pricing/analytics, audit-log, notifications) each react
on their own. EventBus is used for broadcast only — never orchestration (that
stays Temporal's job).

- proto/trips/v1/trip_events.proto: TripCompleted + TripEventHandlers, topic
  "trips.completed" declared via the (connectum.events.v1.event).topic option.
- The publisher uses EventBusOptions.publishes (framework #166) to resolve the
  declared topic with NO raw topic string — the clean replacement for the old
  hand-passed-topic workaround.
- src/events/: publisher + reactor bus factories (NATS via NATS_URL, MemoryAdapter
  injectable for tests) and the 3 reactor routes (idempotent by tripId). Fan-out
  is 3 SEPARATE buses each with a distinct consumer group — forced by the per-bus
  duplicate-topic guard and giving real-broker fan-out (distinct durable
  consumers). src/reactor.ts is the per-reactor process entry (REACTOR=...).
- The publish is a NEW terminal saga activity (publishTripCompleted) in the
  worker. Failure semantics: a success-only try/catch tail OUTSIDE the saga
  try/catch (reached only when SETTLED; its catch does not rethrow) — a failed
  broadcast can NEVER roll back a settled, paid trip. A separate proxyActivities
  block carries its own (delivery-vs-double-fire) retry; reactors are idempotent.
- Tests (dockerless): one publish drives all 3 reactors over a shared
  MemoryAdapter (full TripCompleted shape asserted), the topic resolves to
  "trips.completed" from the proto option, topic isolation, and a D4 guard test
  (broadcast failure leaves the trip SETTLED). 41/41 green.
- docker-compose: nats (JetStream) + 3 reactor processes on the saga profile
  (config-only).

NOTE: depends on @connectum/events.publishes, which ships in 1.1.0 (not yet
published). The example pins ^1.0.0 (resolves to 1.1.0 once published); until
then it builds/runs only with local packages. Merge this AFTER the events 1.1.0
release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MdeH7fExPmiRHRirGuvGk3
@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c349d7c-c0da-49af-a13d-83e07ef54ebc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/car-sharing-eventbus-broadcast

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the type:feature New feature or enhancement request label Jun 21, 2026
@intech

intech commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

Verified the publishes dependency against the #165 pkg.pr.new 1.1.0 snapshots (published-shape, not local tarballs): overrode the whole @connectum/* set to https://pkg.pr.new/@connectum/<pkg>@165 and ran buf generate + typecheck + pnpm test — all green, 41/41, @connectum/events dist carries publishes. So Risk #1 is purely release timing, not a code defect: this example builds/runs against the real published-shape 1.1.0 package. Still draft — un-draft + merge once @connectum/events 1.1.0 is on npm (#165), at which point the ^1.0.0 pin resolves to it automatically.

The publishes option ships in @connectum/events 1.1.0 (a minor bump), not
1.0.1 — fix the README version note accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MdeH7fExPmiRHRirGuvGk3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:feature New feature or enhancement request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant