Skip to main content

SYNC_CONTRACT — bff-tenant-booking-service

Sibling: SERVICE_OVERVIEW · DATA_MODEL

Cross-cutting: 02 Enterprise Architecture §8 · ADR-0003 Electron Offline-First

Important. bff-tenant-booking-service is never consumed by the Electron desktop app. The desktop is the authenticated staff backoffice surface (served by bff-backoffice-service); this BFF is the tenant-branded guest booking surface. No aggregate owned by this service is replicated to any client SQLite store.

1. Replication scope: NONE

Aggregate ownedReplicated to desktop SQLite?Conflict policy
TenantBootstrap (cache)non/a (cloud-only composition cache)
BookingDraftnon/a (Memorystore-only; mobile/web SPAs hold their own client-side state)
BookingHandoffArrivalnon/a (single-use server-side ledger)
MarketingAttributionnon/a (server-side session blob)
LoyaltyContextnon/a (read-through from iam-service, not replicated)
ConfirmationView (cache)non/a (server-side view-model)
BookingDraftSnapshotnon/a (analytics cold mirror, server-side only)

The platform sync registry (@ghasi/sync-protocol/registry.ts) must not include any bff-tenant-booking.* aggregate. CI fails if a row is added.

2. Why this BFF is not in the sync graph

  1. Mobile guest apps don't sync. The tenant-booking mobile app holds short-lived in-memory + local-storage state for the active funnel only. There is no offline booking flow — payment redirects, hold/confirm calls, and quote issuance all require a live network. Loss of connectivity returns the user to a "reconnect to continue" screen.
  2. Booking draft is server-authoritative. The BFF holds the canonical BookingDraft in Memorystore so any tab refresh, page navigation, or mobile-app cold-start can reconstruct the funnel from GET /draft/{id}. The client never owns the draft.
  3. Trust boundary mismatch with the desktop. The Electron desktop's local DB is encrypted at rest with a device-bound key derived through iam-service. The tenant-booking surface is anonymous (Phase 1) or guest-authenticated (Phase 2+); neither has the device-binding root-of-trust.
  4. Money rails require live verification. Confirming a booking depends on payment-gateway-service returning a verified payment status. Replaying that decision offline would either over-charge guests or under-confirm reservations.

3. Cross-BFF handshake (handoff arrival)

The BFF participates in a one-shot inbound handshake with bff-consumer-service over the BookingHandoff token. This is not a sync — it is:

  • Stateless verification (HMAC over a canonical signing string with shared symmetric key from Secret Manager).
  • Single-use (replay-protected via handoff_arrival_log and the consumed boolean).
  • Cross-BFF only (never reaches the desktop or any client SQLite store).
bff-consumer mints ─► signed redirect URL ─► guest browser arrives


POST /handoff/consume
(HMAC verify + consumed flag)


bootstrap returns handoffPayload

The handshake is documented here for completeness because operators sometimes confuse it with sync; it is not.

4. Mobile-app local state (NOT sync, just local cache)

The React Native client holds a small local cache (AsyncStorage) for UX continuity only:

Local keySourceTTLWhat happens on conflict
tnt_idserver-set cookie equivalentuntil app resetn/a (server-authoritative)
last_localelocal prefuntil user changesserver wins on next bootstrap
last_currencylocal prefuntil user changesserver wins on next bootstrap
recent_searchlocal UX7 dserver-side recentlyViewed (Phase 2 if added) wins
bookmarked_propertylocal favuntil user removesmerged on Phase 2 sign-in

None of these are replicated to or from the BFF beyond the standard request/response cycle.

5. Future-state sync candidates (Phase 2+)

CandidateTriggerConflict policy that would applyStatus
Authenticated guest profile (iam-service user)Phase 2 guest sign-inn/a (owned by iam-service, not this BFF)n/a
Multi-device booking-draft resumption (e.g., start on mobile, continue on web)Phase 2server-authoritative draft (no merge needed)Deferred; no design owner
Loyalty wallet (points balance)Phase 2n/a (owned by future loyalty-service)n/a

Even when Phase 2 lands, none of these will sync to the Electron desktop through this BFF; staff workflows live exclusively in bff-backoffice-service.

6. Compliance with the platform sync registry

The @ghasi/sync-protocol/registry.ts file enumerates every aggregate the platform replicates. The CI check sync-registry-allowlist.spec.ts asserts:

import { syncRegistry } from '@ghasi/sync-protocol/registry';

it('does not include bff-tenant-booking aggregates', () => {
for (const entry of syncRegistry) {
expect(entry.serviceName).not.toBe('bff-tenant-booking-service');
}
});

This test runs against every PR in the documentation repo; landing this bundle does not require any registry change.

7. Operational note for SRE

If the platform's per-aggregate conflict-policy matrix in 02 §8.2 is regenerated from per-service SYNC_CONTRACT.mds, this service contributes zero rows. The matrix-builder script must filter on replicatedAggregates.length > 0.