# 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
