Assist_Design/docs/CDC_ONLY_ORDER_IMPLEMENTATION.md
barsa cbaa878000 Refactor Salesforce event handling and caching mechanisms
- Removed the deprecated SF_PROVISION_EVENT_CHANNEL from environment configuration to streamline event handling.
- Enhanced CatalogCdcSubscriber to utilize product IDs for cache invalidation, improving cache management during CDC events.
- Updated OrderCdcSubscriber to trigger provisioning based on Activation_Status__c changes, refining order processing logic.
- Improved CatalogCacheService to support dependency tracking for cached values, enabling more efficient cache invalidation.
- Refactored provisioning processor to simplify job handling and removed unnecessary replay ID management, enhancing clarity and performance.
2025-11-06 17:47:55 +09:00

9.3 KiB

CDC-Only Order Provisioning - Implementation Complete

🎯 What Changed

Your system now uses CDC-only approach for order provisioning. The Platform Event mechanism is no longer needed for order fulfillment.


📊 Architecture Change

Before (Dual Mechanism):

Salesforce Order Status → "Approved"
    ↓
Salesforce Flow publishes Platform Event
    ↓
SalesforcePubSubSubscriber receives event
    ↓
Enqueues provisioning job

After (CDC Only):

Salesforce Order Status → "Approved"
    ↓
Salesforce Flow: set Activation_Status__c = "Activating" (clear errors)
    ↓
CDC: OrderChangeEvent (Activation_Status__c)
    ↓
OrderCdcSubscriber receives event
    ↓
Detects: Activation_Status__c changed to "Activating"
    ↓
Enqueues provisioning job

🔧 Implementation Details

OrderCdcSubscriber Now Handles:

  1. Order Provisioning (NEW)

    • Detects when Salesforce sets Activation_Status__c to "Activating"
    • Validates order is not already provisioning/provisioned
    • Enqueues provisioning job
  2. Cache Invalidation (Existing)

    • Invalidates cache for customer-facing field changes
    • Ignores internal field changes

Key Guards Added:

// 1. Only continue when Activation_Status__c === "Activating"
if (activationStatus !== "Activating") return;

// 2. (Optional) If Status is present, require Approved/Reactivate
if (status && !PROVISION_TRIGGER_STATUSES.has(status)) return;

// 3. Don't trigger if already has WHMCS Order ID
if (whmcsOrderId) return;

📝 Complete Flow Example

Scenario: Admin Approves Order

TIME: 10:00:00 - Admin clicks "Approve" in Salesforce
    ↓
    Salesforce Flow:
      - Sets Activation_Status__c = "Activating"
      - Clears Activation_Error_* fields
    ↓
TIME: 10:00:01 - CDC event published (automatic)
    {
      "Id": "801xxx",
      "Activation_Status__c": "Activating",
      "Activation_Error_Code__c": null,
      "Activation_Error_Message__c": null,
      "changedFields": ["Activation_Status__c", "Activation_Error_Code__c", "Activation_Error_Message__c"]
    }
    ↓
TIME: 10:00:02 - OrderCdcSubscriber.handleOrderEvent()
    ↓
    Step 1: Check if Activation_Status__c changed
      → Yes, value is "Activating"
    ↓
    Step 2: handleActivationStatusChange()
      → activationStatus = "Activating" ✅
      → status (if provided) = "Approved" ✅
      → whmcsOrderId = null ✅ (not provisioned)
    ↓
    Step 3: Enqueue provisioning job
      provisioningQueue.enqueue({
        sfOrderId: "801xxx",
        idempotencyKey: "cdc-activation-1699999999999-801xxx",
        correlationId: "cdc-order-801xxx"
      })
    ↓
    Log: "Order activation moved to Activating via CDC, enqueuing fulfillment"
    ↓
TIME: 10:00:03 - Provisioning processor picks up job
    ↓
    Executes fulfillment (Activation_Status__c already = "Activating" so guard passes)
    ↓
    Updates Salesforce:
      - Status: "Completed"
      - Activation_Status__c: "Activated"
      - WHMCS_Order_ID__c: "12345"
    ↓
TIME: 10:00:05 - CDC events for status updates
    Event 1: Activation_Status__c changed to "Activated"
      → Internal field → Skip cache invalidation ✅
      → Not "Activating" → Skip provisioning ✅

    Event 2: Status → "Completed"
      → Status changed but not "Approved"/"Reactivate" → Skip provisioning ✅
      → Customer-facing field → Invalidate cache ✅

🔍 How It Prevents Duplicate Provisioning

Scenario: Multiple CDC Events

Event 1: Status = "Approved"
  → Guard checks:
    ✅ Status is "Approved" (trigger)
    ✅ activationStatus is null (not provisioning)
    ✅ whmcsOrderId is null (not provisioned)
  → PROVISION ✅

Event 2: Activation_Status__c = "Activating"
  → Guard checks:
    ❌ Status didn't change (not in changedFields)
  → SKIP ✅

Event 3: Status = "Completed", Activation_Status__c = "Activated"
  → Guard checks:
    ❌ Status is "Completed" (not "Approved")
  → SKIP ✅

Scenario: Re-approval After Cancellation

Event 1: Status = "Approved"
  → Provisions order ✅
  → WHMCS_Order_ID__c = "12345"

Event 2: Status = "Cancelled"
  → No provisioning (not "Approved") ✅

Event 3: Status = "Approved" again
  → Guard checks:
    ✅ Status is "Approved"
    ❌ whmcsOrderId = "12345" (already provisioned)
  → SKIP ✅ (prevents duplicate)

⚙️ Configuration

Environment Variables (No Changes Needed)

# CDC is already enabled
SF_EVENTS_ENABLED=true
SF_ORDER_CDC_CHANNEL=/data/OrderChangeEvent
SF_ORDER_ITEM_CDC_CHANNEL=/data/OrderItemChangeEvent

# Platform Event channel can remain (for other uses)
# Or can be removed if not using Platform Events anymore
SF_PROVISION_EVENT_CHANNEL=/event/Order_Fulfilment_Requested__e

Salesforce Setup

  1. Enable CDC on Order object (required)

    Setup → Integrations → Change Data Capture
    Select: Order, OrderItem
    Save
    
  2. Optional: Remove Platform Event Flow

    • The Flow that publishes Order_Fulfilment_Requested__e
    • No longer needed for provisioning
    • Can be deleted or disabled

What You Can Now Do in Salesforce

Simple Approval:

Admin: Sets Order Status = "Approved"
Result: Automatically provisions via CDC ✅

Manual Status Changes:

Admin: Changes Status field directly
  - Draft → Skip
  - Pending Review → Skip
  - Approved → Provision ✅
  - Completed → Skip (invalidate cache only)
  - Cancelled → Skip

Reactivation:

Admin: Sets Status = "Reactivate"
Result: Provisions again (if not already provisioned) ✅

🚨 Important Guards in Place

1. Idempotency Guard

if (whmcsOrderId) {
  // Order already provisioned, don't provision again
  return;
}

2. Status Guard

if (!PROVISION_TRIGGER_STATUSES.has(newStatus)) {
  // Only "Approved" or "Reactivate" trigger provisioning
  return;
}

3. Activation Status Guard

if (activationStatus === "Activating" || activationStatus === "Activated") {
  // Already in progress or done, don't trigger again
  return;
}

📊 Comparison: Platform Event vs CDC

Aspect Platform Event (Before) CDC Only (Now)
Salesforce Setup Need Platform Event + Flow Just enable CDC
Trigger Point Flow publishes (explicit) Status change (automatic)
Complexity Two mechanisms One mechanism
Idempotency Flow handles Guards in Portal
Custom Context Yes (IdemKey, CorrelationId) No (inferred)
Maintenance Flow + Code Code only

🎯 Testing Checklist

Test 1: Normal Approval

# In Salesforce
1. Create Order (Status: "Draft")
2. Set Status = "Approved"

# Expected in Portal logs:
✅ Order status changed to provision trigger via CDC
✅ Successfully enqueued provisioning job
✅ Provisioning job completed

Test 2: Duplicate Prevention

# In Salesforce
1. Order already provisioned (WHMCS_Order_ID__c exists)
2. Set Status = "Approved" again

# Expected in Portal logs:
✅ Order already has WHMCS Order ID, skipping provisioning

Test 3: Cancel Then Approve

# In Salesforce
1. Order Status = "Approved" → Provisions
2. Set Status = "Cancelled"
3. Set Status = "Approved" again

# Expected in Portal logs:
✅ First approval: Provisions
✅ Cancellation: No provisioning
✅ Second approval: Skipped (already has WHMCS_Order_ID__c)

Test 4: Cache Invalidation Still Works

# In Salesforce
1. Update Order.BillingAddress

# Expected in Portal logs:
✅ Order CDC event with customer-facing changes
✅ Invalidating cache

🔄 Rollback Plan (If Needed)

If you need to go back to Platform Events:

  1. Re-enable Salesforce Flow

    • Flow that publishes Order_Fulfilment_Requested__e
  2. Remove provisioning from OrderCdcSubscriber

    • Comment out the handleStatusChange() call
    • Keep cache invalidation logic
  3. Use SalesforcePubSubSubscriber again

    • Already registered in events.module.ts
    • Just needs Flow to publish events

📈 Benefits You Get

Simpler Setup

  • No Platform Event definition needed
  • No Flow to maintain
  • One less mechanism to manage

Automatic Triggering

  • Any Status change to "Approved" provisions
  • No manual event publishing
  • Less room for human error

Still Robust

  • Multiple guards prevent duplicates
  • Idempotency built-in
  • Smart filtering for cache invalidation

Unified CDC Strategy

  • Catalog uses CDC
  • Orders use CDC
  • Consistent architecture

🎓 Summary

Your system now uses CDC-only for order provisioning:

  1. OrderCdcSubscriber triggers provisioning when Status = "Approved"
  2. Multiple guards prevent duplicate provisioning
  3. Cache invalidation still works with smart filtering
  4. Simpler Salesforce setup - just enable CDC
  5. No Platform Event needed for order provisioning

Next Steps:

  1. Enable CDC on Order object in Salesforce
  2. Restart your application
  3. Test by approving an order
  4. Monitor logs for successful provisioning

Your CDC-only implementation is complete and production-ready! 🚀