# Order Status Updates Strategy ## Current Problem The frontend was polling order details every 5-15 seconds, causing: - Unnecessary load on backend (Salesforce API calls, DB queries) - High API costs - Battery drain on mobile devices - Network waste ## Root Cause **Polling is a workaround for lack of real-time push notifications.** When the backend completes provisioning and updates Salesforce, the frontend has no way to know except by repeatedly asking "is it done yet?" ## The Right Solution ### Phase 1: Remove Aggressive Polling ✅ - Polling loop removed from `OrderDetail` (Nov 2025) - Order details now fetched once on mount and whenever the SSE stream notifies of a change - Manual refresh handled via SSE-triggered re-fetch; no background polling left in the page ### Phase 2: Server-Sent Events ✅ (Implemented Nov 2025) **Architecture (now live):** ``` ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ Salesforce │ │ BFF │ │ Frontend │ │ │ │ │ │ │ │ Platform │──Event─▶│ Provisioning│───SSE──│ OrderDetail │ │ Event │ │ Processor │ │ Component │ └─────────────┘ └─────────────┘ └──────────────┘ │ │ 1. Provisions WHMCS │ 2. Updates Salesforce │ 3. Publishes SSE event ▼ ``` **Key pieces:** - `OrderEventsService` keeps per-order subscribers alive, sends heartbeats, and emits `order.update` events - `GET /orders/:sfOrderId/events` exposes the SSE stream (guarded by the same JWT auth pipeline) - `OrderFulfillmentOrchestrator` now publishes updates when Salesforce status flips to `Activating`, when provisioning succeeds, and when failures occur - `useOrderUpdates` hook opens the SSE connection from the browser and triggers a re-fetch of order details when an update arrives - `OrderDetail` now relies on SSE instead of timers (auto-updating as soon as the backend pushes a change) ```apps/bff/src/modules/orders/services/order-events.service.ts @Injectable() export class OrderEventsService { private readonly observers = new Map>(); subscribe(orderId: string): Observable { return new Observable(subscriber => { // ... connection bookkeeping + heartbeat ... }); } publish(orderId: string, update: OrderUpdateEventPayload): void { const event = this.buildEvent("order.update", update); currentObservers.forEach(observer => observer.next(event)); } } ``` ```apps/portal/src/features/orders/views/OrderDetail.tsx const fetchOrder = useCallback(async (): Promise => { if (!params.id) { return; } // ... fetch with AbortController + state updates ... }, [params.id]); const handleOrderUpdate = useCallback( (event: OrderUpdateEventPayload) => { if (!params.id || event.orderId !== params.id) { return; } void fetchOrder(); }, [fetchOrder, params.id] ); useOrderUpdates(params.id, handleOrderUpdate); ``` ### Phase 3: Alternative - WebSockets (More Complex) WebSockets provide bidirectional communication but are more complex to implement and maintain: - Requires WebSocket server setup (socket.io or ws) - More complex authentication - Requires load balancer configuration (sticky sessions) - Better for real-time chat, less ideal for status updates **SSE is simpler and sufficient for status updates.** ## Implementation Priority 1. ✅ Remove aggressive polling (Nov 2025) 2. ✅ Ship SSE-based order updates (Nov 2025) 3. ⏭️ Optional: Consider WebSockets only if you need bidirectional messaging ## Benefits of SSE Approach ✅ **Zero polling** - No unnecessary requests ✅ **Instant updates** - Frontend knows immediately when status changes ✅ **Simple protocol** - SSE is built into browsers, no library needed ✅ **Automatic reconnection** - Browsers handle reconnection automatically ✅ **Lower costs** - Fewer Salesforce API calls ✅ **Better UX** - Users see status changes in real-time ✅ **Less backend load** - No repeated queries ## Migration Path ### Week 1: Remove Polling - [x] Remove aggressive 5-second polling - [x] Document interim strategy ### Week 2: Implement SSE Infrastructure - [x] Create `OrderEventsService` in BFF - [x] Expose SSE endpoint `GET /orders/:sfOrderId/events` - [x] Publish fulfillment lifecycle events (activating, completed, failed) ### Week 3: Frontend Integration - [x] Create `useOrderUpdates` hook - [x] Wire `OrderDetail` to SSE (no timers) - [x] Auto-refetch details on push updates ### Week 4: Post-launch Monitoring - [ ] Add observability for SSE connection counts - [ ] Track client error rates and reconnection attempts - [ ] Review UX analytics after rollout ## Testing Considerations - Test SSE connection drops and automatic reconnection - Test multiple tabs subscribing to same order - Test SSE with load balancer (ensure sticky sessions or shared pub/sub) - Test browser compatibility (SSE supported in all modern browsers) - Monitor SSE connection count and memory usage ## References - [MDN: Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) - [NestJS SSE Documentation](https://docs.nestjs.com/techniques/server-sent-events) - Platform Events (Salesforce → BFF): Already implemented ✅ - SSE (BFF → Frontend): Live in production build --- **Status**: Phase 1 & 2 Complete, Phase 3 (monitoring) in progress **Last Updated**: November 2025 **Owner**: Engineering Team