- Introduced OrderEventsService to manage order-related events and updates. - Added SSE endpoint in OrdersController for streaming order updates to clients. - Enhanced OrderFulfillmentOrchestrator to publish order status updates during fulfillment processes. - Updated order field mappings to include bundled add-on information for improved order detail handling. - Refactored OrderCard and OrderDetail components for better display of order information and status updates.
5.8 KiB
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:
OrderEventsServicekeeps per-order subscribers alive, sends heartbeats, and emitsorder.updateeventsGET /orders/:sfOrderId/eventsexposes the SSE stream (guarded by the same JWT auth pipeline)OrderFulfillmentOrchestratornow publishes updates when Salesforce status flips toActivating, when provisioning succeeds, and when failures occuruseOrderUpdateshook opens the SSE connection from the browser and triggers a re-fetch of order details when an update arrivesOrderDetailnow relies on SSE instead of timers (auto-updating as soon as the backend pushes a change)
@Injectable()
export class OrderEventsService {
private readonly observers = new Map<string, Set<InternalObserver>>();
subscribe(orderId: string): Observable<MessageEvent> {
return new Observable<MessageEvent>(subscriber => {
// ... connection bookkeeping + heartbeat ...
});
}
publish(orderId: string, update: OrderUpdateEvent): void {
const event = this.buildEvent("order.update", update);
currentObservers.forEach(observer => observer.next(event));
}
}
const fetchOrder = useCallback(async (): Promise<void> => {
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
- ✅ Remove aggressive polling (Nov 2025)
- ✅ Ship SSE-based order updates (Nov 2025)
- ⏭️ 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
- Remove aggressive 5-second polling
- Document interim strategy
Week 2: Implement SSE Infrastructure
- Create
OrderEventsServicein BFF - Expose SSE endpoint
GET /orders/:sfOrderId/events - Publish fulfillment lifecycle events (activating, completed, failed)
Week 3: Frontend Integration
- Create
useOrderUpdateshook - Wire
OrderDetailto SSE (no timers) - 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
- NestJS SSE Documentation
- 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