# Salesforce Platform Events & CDC This guide documents all Platform Events and CDC (Change Data Capture) channels used for real-time communication between Salesforce and the Customer Portal. ## Overview The BFF subscribes to Salesforce events using the Pub/Sub API and reacts by: 1. **Invalidating caches** - Ensuring fresh data on next request 2. **Sending SSE events** - Notifying connected portal clients to refetch 3. **Creating in-app notifications** - For significant status changes ## Event Types | Type | Channel Prefix | Purpose | | ------------------- | -------------- | ------------------------------------ | | **Platform Events** | `/event/` | Custom events from Salesforce Flows | | **CDC** | `/data/` | Standard object change notifications | ## Architecture ``` Salesforce Record Change ↓ ┌─────────────────────────────────────────┐ │ Platform Events: Record-Triggered Flow │ │ CDC: Automatic change tracking │ └─────────────────────────────────────────┘ ↓ BFF Pub/Sub Subscriber ↓ ┌────────────────────────────────────────┐ │ 1. Invalidate Redis cache │ │ 2. Send SSE to connected portal │ │ 3. Create notification (if applicable) │ └────────────────────────────────────────┘ ``` --- ## Platform Events ### 1. Account Update Event **Purpose:** Notify portal when eligibility or verification status changes. **Channel:** `SF_ACCOUNT_EVENT_CHANNEL` (default: `/event/Account_Eligibility_Update__e`) #### Fields | Field | API Name | Type | Required | When to Include | | ------------------- | ------------------------ | --------- | --------- | --------------------------------------------------- | | Account ID | `Account_Id__c` | Text(255) | ✅ Always | Always | | Eligibility Status | `Eligibility_Status__c` | Text(255) | Optional | Only if `ISCHANGED(Internet_Eligibility_Status__c)` | | Verification Status | `Verification_Status__c` | Text(255) | Optional | Only if `ISCHANGED(Id_Verification_Status__c)` | | Rejection Message | `Rejection_Message__c` | Text(255) | Optional | Only if Verification_Status\_\_c = "Rejected" | #### Salesforce Flow Logic **Trigger:** Record update on Account when ANY of these fields change: - `Internet_Eligibility__c` - `Internet_Eligibility_Status__c` - `Id_Verification_Status__c` - `Id_Verification_Rejection_Message__c` **Event Payload:** ``` Account_Id__c = {!$Record.Id} IF ISCHANGED({!$Record.Internet_Eligibility_Status__c}) THEN Eligibility_Status__c = {!$Record.Internet_Eligibility_Status__c} END IF IF ISCHANGED({!$Record.Id_Verification_Status__c}) THEN Verification_Status__c = {!$Record.Id_Verification_Status__c} IF {!$Record.Id_Verification_Status__c} = "Rejected" THEN Rejection_Message__c = {!$Record.Id_Verification_Rejection_Message__c} END IF END IF ``` #### BFF Behavior | Scenario | Cache Action | SSE Event | Notification | | -------------------------------------- | --------------- | -------------------- | ------------------------- | | Eligibility value changes (not status) | Invalidate both | ✅ `account.updated` | ❌ | | Eligibility status → Pending | Invalidate both | ✅ `account.updated` | ❌ | | Eligibility status → Eligible | Invalidate both | ✅ `account.updated` | ✅ ELIGIBILITY_ELIGIBLE | | Eligibility status → Ineligible | Invalidate both | ✅ `account.updated` | ✅ ELIGIBILITY_INELIGIBLE | | Verification status → Submitted | Invalidate both | ✅ `account.updated` | ❌ | | Verification status → Verified | Invalidate both | ✅ `account.updated` | ✅ VERIFICATION_VERIFIED | | Verification status → Rejected | Invalidate both | ✅ `account.updated` | ✅ VERIFICATION_REJECTED | | Rejection message changes only | Invalidate both | ✅ `account.updated` | ❌ | --- ### 2. Case Status Update Event **Purpose:** Notify portal when a support case status changes. **Channel:** `SF_CASE_EVENT_CHANNEL` (default: `/event/Case_Status_Update__e`) #### Fields | Field | API Name | Type | Required | | ---------- | --------------- | --------- | --------- | | Account ID | `Account_Id__c` | Text(255) | ✅ Always | | Case ID | `Case_Id__c` | Text(255) | ✅ Always | #### Salesforce Flow Logic **Trigger:** Record update on Case when Status changes (and Origin = "Portal Support") **Event Payload:** ``` Account_Id__c = {!$Record.AccountId} Case_Id__c = {!$Record.Id} ``` #### BFF Behavior | Action | Description | | -------------------------------------- | ------------------------- | | Invalidate `support:cases:{accountId}` | Clear case list cache | | Invalidate `support:messages:{caseId}` | Clear case messages cache | | SSE `support.case.changed` | Notify portal to refetch | --- ### 3. Order Provision Requested Event **Purpose:** Trigger order fulfillment when Salesforce Order is approved. **Channel:** `/event/OrderProvisionRequested__e` See `docs/how-it-works/order-fulfillment.md` for details. --- ## CDC (Change Data Capture) ### 1. Product2 CDC **Purpose:** Detect product catalog changes. **Channel:** `SF_CATALOG_PRODUCT_CDC_CHANNEL` (default: `/data/Product2ChangeEvent`) #### BFF Behavior | Action | Description | | -------------------------- | --------------------------------- | | Invalidate product cache | Clear affected products | | Fallback full invalidation | If no specific products found | | SSE `services.changed` | Notify portals to refetch catalog | --- ### 2. PricebookEntry CDC **Purpose:** Detect pricing changes. **Channel:** `SF_CATALOG_PRICEBOOKENTRY_CDC_CHANNEL` (default: `/data/PricebookEntryChangeEvent`) #### BFF Behavior | Action | Description | | ------------------------ | ------------------------------------- | | Filter by pricebook | Only process portal pricebook changes | | Invalidate product cache | Clear affected products | | SSE `services.changed` | Notify portals to refetch catalog | --- ### 3. Order CDC **Purpose:** Detect order changes + trigger provisioning. **Channel:** `SF_ORDER_CDC_CHANNEL` (default: `/data/OrderChangeEvent`) #### BFF Behavior | Scenario | Action | | ------------------------------------- | ------------------------------ | | `Activation_Status__c` → "Activating" | Enqueue provisioning job | | Customer-facing field changes | Invalidate order cache + SSE | | Internal field changes only | Ignore (no cache invalidation) | **Internal fields (ignored):** `Activation_Status__c`, `WHMCS_Order_ID__c`, `Activation_Error_*` --- ### 4. OrderItem CDC **Purpose:** Detect order line item changes. **Channel:** `SF_ORDER_ITEM_CDC_CHANNEL` (default: `/data/OrderItemChangeEvent`) #### BFF Behavior | Scenario | Action | | ----------------------------- | ------------------------------ | | Customer-facing field changes | Invalidate order cache + SSE | | Internal field changes only | Ignore (no cache invalidation) | **Internal fields (ignored):** `WHMCS_Service_ID__c` --- ## Environment Variables ```bash # Platform Events SF_ACCOUNT_EVENT_CHANNEL=/event/Account_Eligibility_Update__e SF_CASE_EVENT_CHANNEL=/event/Case_Status_Update__e # Catalog CDC SF_CATALOG_PRODUCT_CDC_CHANNEL=/data/Product2ChangeEvent SF_CATALOG_PRICEBOOKENTRY_CDC_CHANNEL=/data/PricebookEntryChangeEvent # Order CDC SF_ORDER_CDC_CHANNEL=/data/OrderChangeEvent SF_ORDER_ITEM_CDC_CHANNEL=/data/OrderItemChangeEvent # Enable/disable all CDC subscriptions (Platform Events always enabled) SF_EVENTS_ENABLED=true ``` --- ## BFF Implementation ### Subscribers | Subscriber | Events | Channel | File | | ------------------------- | ------------------------ | --------- | ------------------------------ | | `AccountEventsSubscriber` | Account | `/event/` | `account-events.subscriber.ts` | | `CaseEventsSubscriber` | Case | `/event/` | `case-events.subscriber.ts` | | `CatalogCdcSubscriber` | Product2, PricebookEntry | `/data/` | `catalog-cdc.subscriber.ts` | | `OrderCdcSubscriber` | Order, OrderItem | `/data/` | `order-cdc.subscriber.ts` | ### Shared Utilities (`shared/`) | File | Purpose | | ------------------- | ------------------------------------ | | `pubsub.service.ts` | Salesforce Pub/Sub client management | | `pubsub.utils.ts` | Payload extraction helpers | | `pubsub.types.ts` | TypeScript types | | `index.ts` | Public exports | --- ## Testing 1. **Salesforce Workbench:** Use "REST Explorer" to publish test events 2. **BFF Logs:** Watch for event received messages 3. **Redis:** Verify cache keys are deleted after event ## Troubleshooting | Issue | Cause | Solution | | ------------------------ | ------------------------- | ----------------------------------------- | | Events not received | Pub/Sub connection failed | Check BFF logs for connection errors | | Cache not invalidated | Wrong ID in payload | Verify Flow is sending correct ID | | Notification not created | Status not in final state | Only final states trigger notifications | | Duplicate notifications | Same event re-delivered | Dedupe logic handles this (1 hour window) |