269 lines
10 KiB
Markdown
269 lines
10 KiB
Markdown
|
|
# 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) |
|