feat(car-sharing): EventBus broadcast / fan-out on trip completion (Phase 3)#29
feat(car-sharing): EventBus broadcast / fan-out on trip completion (Phase 3)#29intech wants to merge 2 commits into
Conversation
…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
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
Verified the |
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
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,
TripCompletedis published once and consumed independently by 3 reactors (pricing/analytics, audit-log, notifications). EventBus is for broadcast only — orchestration stays Temporal's job.ctx.callHow
proto/trips/v1/trip_events.proto—TripCompleted+TripEventHandlers, topictrips.completedvia the(connectum.events.v1.event).topicoption.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 anypublish().tripId.publishTripCompletedin the worker. Failure semantics: a success-onlytry/catchtail outside the saga try/catch (reached only whenSETTLED; its catch does not rethrow) — a failed broadcast can never roll back a settled, paid trip. SeparateproxyActivitiesretry block for the publish.Tests (dockerless)
One publish drives all 3 reactors over a shared
MemoryAdapter(fullTripCompletedshape asserted), the topic resolves totrips.completedfrom the proto option, topic isolation, and a guard test (broadcast failure leaves the tripSETTLED).buf generate/lint+typecheckexit 0;pnpm test41/41 (all Phase-2 suites still green). docker-composenats(JetStream) + 3 reactor processes on thesagaprofile are config-only.🤖 Generated with Claude Code
https://claude.ai/code/session_01MdeH7fExPmiRHRirGuvGk3