Assist_Design/docs/CDC_ONLY_ORDER_IMPLEMENTATION.md
barsa 309dac630f Enhance order processing and caching mechanisms
- Introduced provisioning triggers in OrderCdcSubscriber to handle specific status changes and enqueue provisioning jobs.
- Implemented request coalescing in OrdersCacheService and CatalogCacheService to prevent duplicate Salesforce API calls during cache invalidation.
- Updated CatalogModule and OrdersModule to export additional services for improved module integration.
- Enhanced error handling and logging in various services to provide better insights during operations.
2025-11-06 17:01:34 +09:00

9.0 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"
    ↓
CDC: OrderChangeEvent (automatic)
    ↓
OrderCdcSubscriber receives event
    ↓
Detects: Status changed to "Approved"
    ↓
Enqueues provisioning job

🔧 Implementation Details

OrderCdcSubscriber Now Handles:

  1. Order Provisioning (NEW)

    • Detects when Status changes to "Approved" or "Reactivate"
    • 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 trigger for specific statuses
PROVISION_TRIGGER_STATUSES = ["Approved", "Reactivate"]

// 2. Don't trigger if already provisioning
if (activationStatus === "Activating") return;

// 3. Don't trigger if already activated
if (activationStatus === "Activated") return;

// 4. 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
    ↓
    Order Status: "Pending Review" → "Approved"
    ↓
TIME: 10:00:01 - CDC event published (automatic)
    {
      "Id": "801xxx",
      "Status": "Approved",
      "Activation_Status__c": null,
      "WHMCS_Order_ID__c": null,
      "changedFields": ["Status"]
    }
    ↓
TIME: 10:00:02 - OrderCdcSubscriber.handleOrderEvent()
    ↓
    Step 1: Check if Status field changed
      → Yes, Status in changedFields
    ↓
    Step 2: handleStatusChange()
      → newStatus = "Approved" ✅
      → activationStatus = null ✅ (not provisioning)
      → whmcsOrderId = null ✅ (not provisioned)
    ↓
    Step 3: Enqueue provisioning job
      provisioningQueue.enqueue({
        sfOrderId: "801xxx",
        idempotencyKey: "cdc-status-1699999999999-801xxx",
        correlationId: "cdc-order-801xxx"
      })
    ↓
    Log: "Order status changed to provision trigger via CDC"
    ↓
TIME: 10:00:03 - Provisioning processor picks up job
    ↓
    Executes fulfillment
    ↓
    Updates Salesforce:
      - Activation_Status__c: "Activating"
      - Then: "Activated"
      - WHMCS_Order_ID__c: "12345"
      - Status: "Completed"
    ↓
TIME: 10:00:05 - CDC events for status updates
    Event 1: Activation_Status__c changed
      → OrderCdcSubscriber checks
      → Is internal field → Skip cache invalidation ✅
      → Status didn't change → Skip provisioning ✅
    
    Event 2: Status → "Completed"
      → OrderCdcSubscriber checks
      → Status changed but not "Approved" → 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! 🚀