Conversation
sjvans
commented
Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
xflights sibling: capire/xflights#9
The Saga Pattern Story
The xtravels app demonstrates a choreography-based saga for distributed travel booking, spanning two microservices: xtravels (the booking owner) and xflights (the seat inventory manager).
The Flow
Step 1 — Local commit with pending state
When a customer saves a Travel with Bookings, xtravels commits the data locally. Each Booking is initially in an unresolved state — neither confirmed nor failed.
Step 2 — Outboxed event emission
After the save, xtravels emits a
BookingCreatedevent to xflights via the transactional outbox (cds.outboxed(xflights)). The outbox decouples the emission from the local transaction: the event is durably stored and delivered reliably, even if xflights is temporarily unavailable.Step 3 — Remote processing
xflights receives the event, checks seat availability, and updates the occupied seat count. If no seats are available it calls
req.reject(...), which causes the outbox runtime to emitBookingCreated/#failedback to the caller; on success it emitsBookingCreated/#succeeded.Step 4a — Confirmation
On success, xtravels updates the Booking status to Confirmed (
'C'):Step 4b — Failure recording
On failure (e.g. no seats available), xtravels marks the Booking as Failed (
'F'). This is not a rollback — the booking record was never confirmed, so there is nothing to undo. Instead the failure is recorded explicitly, leaving it visible to the user for manual resolution or retry:Why this is a Saga
There is no distributed transaction. Instead, each service owns its local data and emits events. Consistency is achieved eventually through the event chain:
The transactional outbox (
cds.outboxed) is what makes this reliable: the event is guaranteed to be delivered exactly once, even across restarts, so the saga never gets stuck in a half-sent state.The compensation (setting
Status_code: 'F') is explicit and domain-meaningful — the booking was never confirmed, so there is nothing to roll back. Recording the failure leaves it visible to the user, enabling manual resolution or retry.Replicated Flight Data — Keeping Free Seats in Sync
xtravels keeps a local replica of flight data (including
free_seats) for fast value-help lookups without round-tripping to xflights on every request.The replica is initialized and kept current via two complementary mechanisms:
Scheduled delta replication (every 10 minutes)
The
Flightsentity in xtravels is annotated@federated. The genericdata-federation.jsmodule picks up all@federatedentities at startup, turns them into local persistence tables, and schedules a periodic delta replication:Each run reads only rows modified since the last replication (
modifiedAt > latest) and upserts them locally — a low-traffic catch-all that ensures the replica stays consistent even if events are missed.Real-time updates via
FlightsUpdatedeventsxflights emits
FlightsUpdatedevents whenever seat counts change, and xtravels subscribes to keep its replica current — only when it actually holds a local persistence table forFlights:The event is treated as a pure notification ("something changed"). Rather than trusting a seat count carried in the payload — which could be stale if two updates arrive out of order — xtravels re-reads the authoritative, current value from xflights.
The CAP Primitives at Work
cds.outboxed(xflights)BookingCreated/#succeeded//#failedUPDATE ... set Status_code: 'F'@federated+ delta replication every 10 minxflights.on('FlightsUpdated', ...)+ re-readThe pattern keeps each service's data locally consistent at all times. Cross-service consistency emerges from the event chain — no distributed transactions, no central orchestrator.