背景と適用シナリオ
Salesforce Loyalty Management は、顧客エンゲージメントを高め、ブランドへの忠誠心を育むための強力なプラットフォームです。標準機能だけでも、ポイントの付与・交換、会員ティアの管理など、包括的なロイヤルティプログラムを構築できます。しかし、ビジネスの要件は多岐にわたり、時には標準機能だけでは対応しきれない複雑なシナリオが発生します。
例えば、以下のようなケースが考えられます。
- 動的なボーナスポイント付与:特定のキャンペーン期間中に、特定の商品を購入したVIP会員に対してのみ、購入金額の1.5倍のボーナスポイントを付与する。
- 外部システム連携によるポイント調整:提携する外部サービスの利用状況に応じて、リアルタイムでポイントを付与または減算する。
- 複雑なティア昇格ロジック:過去3ヶ月間の購入金額と来店回数の両方を組み合わせた、独自の複雑なロジックで会員ティアを決定する。
- 不正利用の検知:短時間に異常な数のポイント獲得トランザクションが発生した場合、処理を一時停止し、管理者に通知する。
このような独自のビジネスロジックを実装するためには、Salesforceプラットフォームの柔軟性を最大限に活用する必要があります。本記事では、Salesforce 開発者の視点から、Flow(フロー)と Apex(Salesforce独自のプログラミング言語)を組み合わせて Loyalty Management の機能を拡張し、複雑な要件に対応する方法を技術的に解説します。
原理説明
Salesforce Loyalty Management のカスタマイズは、主に Transaction Journal(取引ジャーナル)オブジェクトを起点として行われます。このオブジェクトは、ポイントの獲得や交換といったすべてのロイヤルティ関連の活動を記録する中心的な役割を担います。
基本的な処理フローは以下のようになります。
- トランザクションの発生:顧客が商品を購入したり、特定のアクションを完了したりすると、その情報が Salesforce に連携され、新しい Transaction Journal レコードが作成されます。このレコードには、活動日、関連する会員情報、ポイント数などの詳細が含まれます。
- 自動化プロセスの起動:Transaction Journal レコードが作成または更新されると、それをトリガーとして Record-Triggered Flow(レコードトリガーフロー)が起動します。
- ビジネスロジックの実行:Flow内で、条件分岐や数式を用いて標準的なロジックを処理します。しかし、より複雑な計算や外部APIの呼び出しが必要な場合は、Flowから Invocable Apex Method(呼び出し可能なApexメソッド)を呼び出します。
- ロイヤルティエンジンの実行:Apexクラス内で、Loyalty Management が提供する標準の LoyaltyEngine を呼び出します。このエンジンが、Transaction Journal の情報に基づいて実際のポイント計算、Loyalty Ledger(ロイヤルティ台帳)への記録、会員ティアの評価といった一連の処理を実行します。
- 結果の反映:処理が完了すると、Loyalty Ledger が更新され、会員のポイント残高やティア情報が最新の状態に反映されます。
このアーキテクチャの鍵は、Flow の宣言的な使いやすさと、Apex のプログラム的な柔軟性を組み合わせる点にあります。開発者は、Flow でプロセスの全体像を設計し、ロジックが複雑化する部分だけを Apex に委譲することで、保守性と拡張性の高いソリューションを構築できます。
主要なオブジェクト
- LoyaltyProgram: ロイヤルティプログラム全体の定義を格納します。
- LoyaltyProgramMember: プログラムに参加している各顧客(会員)の情報を管理します。
- TransactionJournal: すべてのロイヤルティ関連トランザクション(ポイント獲得、交換など)を記録するジャーナルです。カスタマイズの主要なトリガーオブジェクトとなります。
- LoyaltyLedger: 会員のポイント残高を記録する台帳です。Transaction Journal の処理結果がここに反映されます。
サンプルコード
ここでは、Transaction Journal レコードが作成された際に、Flow から Invocable Apex を呼び出して非同期で処理を実行するシナリオを想定します。大量のトランザクションを処理する場合、同期処理では Governor Limit(ガバナ制限)に抵触する可能性があるため、非同期処理が有効な選択肢となります。
以下の Apex クラスは、Transaction Journal の ID リストを Flow から受け取り、`ConnectApi.LoyaltyManagement.processTransactionJournals` メソッドを使用して非同期でジャーナルを処理します。このコードは Salesforce の公式開発者ドキュメントに基づいています。
AsyncLoyaltyJournalProcessor.cls
public with sharing class AsyncLoyaltyJournalProcessor { // InvocableMethodアノテーションにより、このメソッドをFlowから呼び出し可能にする @InvocableMethod(label='Process Transaction Journals Asynchronously' description='Processes a list of Transaction Journal Ids using the Loyalty Management engine asynchronously.') public static void processJournals(List<Id> journalIds) { // 処理対象のIDが空でないことを確認 if (journalIds == null || journalIds.isEmpty()) { System.debug('Journal IDs list is empty. No action taken.'); return; } // ConnectApiの入力パラメータを準備 // このクラスは、処理するジャーナルIDとエラー発生時の処理方法を定義します ConnectApi.ProcessTransactionJournalInput input = new ConnectApi.ProcessTransactionJournalInput(); input.journalIds = journalIds; // エラーが発生した場合でも、処理可能なジャーナルは継続して処理する input.errorProcessingStrategy = ConnectApi.LoyaltyErrorProcessingStrategy.ProcessSuccessfullyAndSkipErrors; try { // LoyaltyManagementのConnect APIを呼び出し、非同期でトランザクションジャーナルを処理する // このメソッドは非同期ジョブのIDを返す String asyncJobId = ConnectApi.LoyaltyManagement.processTransactionJournals(input); System.debug('Successfully enqueued asynchronous job for Transaction Journals. Job ID: ' + asyncJobId); // 必要に応じて、返されたジョブIDをカスタムオブジェクトに保存し、 // バッチ処理のステータスを追跡することも可能 // 例: LogAsyncJob(jobId, journalIds); } catch (Exception e) { // 例外が発生した場合、エラーをデバッグログに出力する // 本番環境では、Platform Eventやカスタムオブジェクトにエラーを記録し、 // 管理者に通知する仕組みを実装することが推奨される System.debug('Error calling LoyaltyManagement.processTransactionJournals: ' + e.getMessage()); // 例外を再スローすることで、FlowのFault Path(障害パス)でエラーを捕捉できる throw new AuraHandledException('Failed to enqueue loyalty journal processing job. Reason: ' + e.getMessage()); } } }
この Apex クラスをデプロイした後、Transaction Journal オブジェクトの作成をトリガーとする Record-Triggered Flow を作成します。Flow の中で「アクション」要素を追加し、この `AsyncLoyaltyJournalProcessor` クラスを選択することで、カスタムロジックを簡単に組み込むことができます。
注意事項
Loyalty Management のカスタマイズを実装する際には、以下の点に注意する必要があります。
権限 (Permissions)
Apex コードや Flow を実行するユーザーには、適切な権限が必要です。特に、ロイヤルティエンジンを呼び出すには、`Loyalty Management - Process Engine` 権限セットが割り当てられている必要があります。また、関連オブジェクト(TransactionJournal, LoyaltyLedgerなど)への参照・更新権限も必須です。
API 制限 (API Limits) とガバナ制限 (Governor Limits)
Apex コードは Salesforce のマルチテナント環境で実行されるため、厳格なガバナ制限に従う必要があります。
- SOQL クエリ:1トランザクションあたり100件のSOQLクエリ制限。コードは必ず Bulkified(一括処理)されるように設計し、ループ内でのSOQLクエリ発行を避ける必要があります。
- CPU 時間:複雑な計算ロジックは CPU 時間を消費します。処理が10秒(同期)または60秒(非同期)を超えると、トランザクションは強制終了されます。
- `ConnectApi.LoyaltyManagement.processTransactionJournals` の制限:このメソッドは、1回の呼び出しで最大 200件 のジャーナルIDを処理できます。200件を超える場合は、リストを分割して複数回呼び出す必要があります。
大量のトランザクションを処理する場合は、サンプルコードで示したような非同期処理(@future, Queueable Apex, Batch Apex)を積極的に活用し、制限を回避することが重要です。
エラー処理 (Error Handling)
ロイヤルティプログラムは金銭的価値を持つポイントを扱うため、堅牢なエラー処理が不可欠です。
- Try-Catch ブロック:Apex コード内では、必ず `try-catch` ブロックを使用して予期せぬ例外を捕捉し、適切に処理してください。
- Flow の障害パス (Fault Path):Flow で Apex アクションを呼び出す際には、障害パスを構成し、エラーが発生した場合の代替処理(例:管理者にメール通知、Transaction Journal のステータスを「エラー」に更新)を定義しておくべきです。
- ログ記録:エラーが発生した際には、その原因とコンテキスト(どの会員のどのトランザクションで問題が起きたか)を特定できるよう、カスタムオブジェクトや Platform Event(プラットフォームイベント)に詳細なログを記録する仕組みを構築することを強く推奨します。
まとめとベストプラクティス
Salesforce Loyalty Management は、Flow と Apex を組み合わせることで、その可能性を飛躍的に高めることができます。標準機能で要件の8割をカバーし、残りの2割の複雑な独自要件をカスタム開発で補うことで、企業独自の価値あるロイヤルティプログラムを実現できます。
開発者として成功するためのベストプラクティスは以下の通りです。
- 宣言的アプローチを優先する (Declarative First): 可能な限り Flow や標準機能でロジックを構築し、それが困難な場合にのみ Apex の使用を検討します。これにより、開発・保守コストを最小限に抑えることができます。
- スケーラビリティを考慮した設計: 将来的にトランザクション量が数百万件に増加する可能性を念頭に置き、常に一括処理と非同期処理を意識した設計を心がけてください。
- テストの徹底: カスタムロジックは、ポジティブシナリオ(正常系)とネガティブシナリオ(異常系)の両方で十分にテストする必要があります。Apex テストクラスを作成し、コードカバレッジを高く維持するだけでなく、様々なデータパターンでロジックの正当性を検証してください。
- データモデルの深い理解: Transaction Journal がどのように Loyalty Ledger に影響を与えるか、また、その他の関連オブジェクト(`LoyaltyTier`, `Voucher`など)との関係性を正確に理解することが、効果的なカスタマイズの鍵となります。
- エラーハンドリングと監視: 処理の失敗は必ず起こるものと想定し、失敗を迅速に検知し、原因を特定し、手動または自動でリカバリーできる仕組みをあらかじめ組み込んでおくことが、信頼性の高いシステムを構築する上で不可欠です。
これらの原則に従うことで、Salesforce 開発者は Loyalty Management プラットフォームを最大限に活用し、顧客とビジネスの双方に大きな価値をもたらす、洗練されたロイヤルティソリューションを構築することができるでしょう。
コメント
コメントを投稿