- Streamlined the README.md for clarity and conciseness. - Deleted outdated documentation files related to Freebit SIM management, SIM management API data flow, and various architectural guides to reduce clutter and improve maintainability. - Updated the last modified date in the README to reflect the latest changes.
163 lines
5.8 KiB
Markdown
163 lines
5.8 KiB
Markdown
# 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<string, Set<InternalObserver>>();
|
|
|
|
subscribe(orderId: string): Observable<MessageEvent> {
|
|
return new Observable<MessageEvent>(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<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
|
|
|
|
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
|