Apexを活用した高度なSalesforce重複管理:開発者向けガイド

背景と応用シナリオ

Salesforce環境において、データの品質はビジネスの成功を左右する重要な要素です。特に、重複データは営業効率の低下、マーケティング活動の無駄、不正確なレポート、そして顧客満足度の低下など、多くの問題を引き起こします。Salesforceは標準機能として強力な Duplicate Management (重複管理) 機能を提供しており、Matching Rule (一致ルール)Duplicate Rule (重複ルール) を組み合わせることで、ユーザーがレコードを作成・更新する際の重複をリアルタイムで検知し、ブロックまたは警告することが可能です。

しかし、標準機能だけでは対応しきれない複雑なシナリオも存在します。私たちSalesforce開発者の出番は、まさにそのような時です。

応用シナリオ

  • 複雑なビジネスロジック: 標準の一致ルールでは表現できない、複数のオブジェクトをまたがるような複雑な条件で重複を判定する必要がある場合。
  • 外部システム連携: API経由で外部システムから大量のデータが連携される際に、データをSalesforceに挿入する前に重複チェックを行い、既存レコードの更新や、重複のマージ処理をプログラムで自動化したい場合。
  • カスタムUIの実装: 標準の重複警告画面ではなく、独自のUIコンポーネント(LWCやAura)上で、よりリッチな重複候補の表示やマージ操作を提供したい場合。
  • バッチ処理によるデータクレンジング: 既存のデータベースに存在する大量の重複データを、夜間バッチなどで定期的に特定し、自動的にマージまたは削除するプロセスを構築する場合。

本記事では、Salesforce開発者の視点から、Apexを用いてこれらの高度な重複管理要件を実現する方法について、その原理から具体的なコード例、注意点までを詳しく解説します。


原理説明

Apexによる重複管理の核心は、SalesforceのDML (Data Manipulation Language) 操作と、重複ルールがどのように連携して動作するかを理解することにあります。開発者が利用できる主要なメカニズムは2つあります。

1. Database.DmlOptionsとDuplicateRuleHeader

Salesforceでは、標準のinsertupdateといったDMLステートメントは、有効な重複ルールに違反すると例外をスローして処理を停止させます。しかし、Databaseクラスのメソッドを使用すると、DML操作の挙動をより細かく制御できます。その鍵となるのが Database.DmlOptions クラスです。

このクラスのプロパティである DuplicateRuleHeader を設定することで、DML操作時に重複ルールをどのように扱うかを指定できます。

  • allowSave: trueに設定すると、重複が検知されてもレコードの保存を許可します。これは「警告(Alert)」設定の重複ルールと同様の挙動をコードで実現するものです。デフォルトはfalseで、重複が検知されるとエラーになります(「ブロック(Block)」設定と同様)。
  • includeRecordDetails: trueに設定すると、DMLの結果(Database.SaveResult)に、重複したレコードの詳細情報(IDなど)が含まれるようになります。これにより、どのレコードと重複したのかをプログラムで特定できます。
  • runAsCurrentUser: trueに設定すると、重複ルールが現在のユーザーの共有ルール(Sharing Rules)を考慮して実行されます。デフォルトはfalseで、システムコンテキストで実行され、ユーザーがアクセスできないレコードも重複チェックの対象となります。

この仕組みを利用することで、開発者は重複を検知しつつも、例外で処理を中断させることなく、後続のカスタムロジック(例えば、重複レコード情報をログに記録する、特定の項目を更新する、関連レコードを付け替えるなど)を実行できます。

2. SOQLによる能動的な重複検出

DML操作時のリアクティブな対応だけでなく、データベース内に存在する重複を能動的に見つけ出すことも重要です。これには SOQL (Salesforce Object Query Language) の集計関数が非常に有効です。

例えば、同じメールアドレスを持つ取引先責任者を特定するには、以下のようなクエリを使用します。

SELECT COUNT(Id), Email
FROM Contact
WHERE Email != null
GROUP BY Email
HAVING COUNT(Id) > 1

このクエリは、同じEmailを持つレコードのグループを作成し、そのグループ内のレコード数が1より大きいもの(つまり重複しているもの)だけを返します。この結果を元に、Batch Apexなどの非同期処理を用いて、大量の重複レコードに対するクレンジング処理を実装することが可能です。

3. Datacloud.FindDuplicatesクラス

より高度な方法として、Apexから直接、既存の一致ルールを利用して重複を検索する Datacloud.FindDuplicates クラスがあります。このクラスのfindDuplicatesメソッドは、sObjectのリストを引数として受け取り、それらのレコードと重複する可能性のある既存レコードを、設定済みのマッチングルールに基づいて返します。カスタムUIで「このレコードと似ているレコードはありますか?」といった機能を提供する際に非常に強力です。


示例代码

ここでは、最も一般的なシナリオである「Apexでレコードを挿入する際に重複ルールをハンドリングする方法」を、Database.DmlOptionsを使用した公式ドキュメントのコード例を基に解説します。

このコードは、重複の可能性がある取引先(Account)レコードを挿入しようとし、重複が検知された場合にその情報を取得してデバッグログに出力する例です。

コード例:重複ルールを考慮した取引先の挿入

// DML操作のオプションを管理するDmlOptionsインスタンスを作成します。
Database.DmlOptions dmlOptions = new Database.DmlOptions();

// 重複ルールヘッダーを設定します。
// allowSaveをtrueにすることで、重複が検知されてもレコードの保存を許可します。
// これにより、例外で処理が停止するのを防ぎます。
dmlOptions.DuplicateRuleHeader.allowSave = true;

// includeRecordDetailsをtrueに設定し、DMLの結果に重複レコードの詳細情報を含めるようにします。
dmlOptions.DuplicateRuleHeader.includeRecordDetails = true;

// runAsCurrentUserをtrueに設定し、現在のユーザーの権限で重複ルールを実行します。
dmlOptions.DuplicateRuleHeader.runAsCurrentUser = true;

// 重複の可能性がある取引先レコードを作成します。
// 組織内にすでに 'Salesforce' という名前の取引先が存在すると仮定します。
List<Account> accList = new List<Account>{
    new Account(Name='Salesforce')
};

// Database.insertメソッドを使用し、作成したDMLオプションを渡します。
// 第2引数の allOrNone を false に設定すると、一部のレコードが失敗しても他のレコードはコミットされます。
// 今回はレコードが1つなので true でも false でも挙動は同じですが、リスト処理では false が一般的です。
Database.SaveResult[] srList = Database.insert(accList, dmlOptions);

// DML操作の結果をループで確認します。
for (Database.SaveResult sr : srList) {
    if (sr.isSuccess()) {
        // 挿入が成功した場合の処理
        System.debug('Successfully inserted account with ID: ' + sr.getId());
    } else {
        // 挿入が失敗した場合(重複を含む)の処理
        for (Database.Error err : sr.getErrors()) {
            System.debug('The following error has occurred.');
            System.debug(err.getStatusCode() + ': ' + err.getMessage());
            System.debug('Account fields that affected this error: ' + err.getFields());

            // エラーが重複エラーであるかを確認します。
            if (err instanceof Database.DuplicateError) {
                Database.DuplicateError duplicateError = (Database.DuplicateError)err;
                Datacloud.DuplicateResult duplicateResult = duplicateError.getDuplicateResult();

                System.debug('Duplicate Rule: ' + duplicateResult.getDuplicateRule());
                System.debug('Duplicate Rule Developer Name: ' + duplicateResult.getDuplicateRuleDeveloperName());
                
                // 重複したレコードに関する情報を取得します。
                Datacloud.MatchResult[] matchResults = duplicateResult.getMatchResults();
                for (Datacloud.MatchResult matchResult : matchResults) {
                    System.debug('Match Engine: ' + matchResult.getMatchEngine());
                    
                    Datacloud.MatchRecord[] matchRecords = matchResult.getMatchRecords();
                    for (Datacloud.MatchRecord matchRecord : matchRecords) {
                        System.debug('Matching Record ID: ' + matchRecord.getRecord().Id);
                    }
                }
            }
        }
    }
}

注意事項

Apexによる重複管理を実装する際には、以下の点に注意する必要があります。

権限 (Permissions)

Apexは通常、システムモードで実行されるため、実行ユーザーの項目レベルセキュリティやオブジェクト権限を無視します。しかし、Database.DmlOptionsrunAsCurrentUsertrueに設定した場合、重複ルールの評価は実行ユーザーの共有設定に基づいて行われます。ユーザーがアクセスできないレコードは重複チェックの対象から外れる可能性があるため、ビジネス要件に応じてこの設定を慎重に選択する必要があります。

API制限 (API Limits)

Apexの実行には、ご存知の通り Governor Limits (ガバナ制限) が適用されます。特に、バッチ処理で大量の重複データを処理する際には、以下の制限に注意が必要です。

  • SOQLクエリ: 1トランザクションあたりの発行回数は100回まで(同期)、200回まで(非同期)。重複検索のロジックが複雑で、ループ内でSOQLを発行しないように注意が必要です。
  • DMLステートメント: 1トランザクションあたりの実行回数は150回まで。必ずリスト(List)を使って一括でDML操作を行う「バルク化」を徹底してください。
  • CPU時間: 複雑な重複判定ロジックはCPU時間を消費します。処理を効率化し、制限を超えないように設計する必要があります。

また、Datacloud.FindDuplicates.findDuplicates()メソッドにも、1トランザクションあたりの呼び出し回数や一度に処理できるレコード数に独自の制限が存在します。実装前に必ず最新の公式ドキュメントで制限値を確認してください。

エラー処理 (Error Handling)

DML操作の結果は必ずしも成功するとは限りません。特に重複管理においては、重複がブロックされたり、その他のバリデーションルールでエラーが発生したりすることが想定されます。Database.insert(records, false)のように、第2引数をfalseに設定して部分的な成功を許容し、返り値のDatabase.SaveResultの配列を一つずつチェックして、どのレコードが成功し、どのレコードがなぜ失敗したのかを正確にハンドリングすることが極めて重要です。失敗したレコードの情報はログに記録し、後の調査や再処理に役立てるべきです。


まとめとベストプラクティス

Salesforce開発者として、Apexを活用することで、標準機能の枠を超えた高度で柔軟な重複管理ソリューションを構築できます。

以下に、成功のためのベストプラクティスをまとめます。

  1. 宣言的アプローチを第一に: まずは標準のMatching RuleとDuplicate Ruleで要件を満たせないか最大限検討します。設定だけで実現できることは、コードを書くよりもメンテナンス性に優れています。
  2. Apexは「最後の砦」として活用: 標準機能で対応できない複雑なロジック、外部連携、カスタムUI、大規模なデータクレンジングといったシナリオに限定してApexを使用します。
  3. 非同期処理を積極的に利用: 既存データのクレンジングなど、大量のデータを扱う場合は、必ずBatch ApexやQueueable Apexといった非同期処理フレームワークを利用し、ガバナ制限を回避します。
  4. テストカバレッジの徹底: 重複が発生するケース、しないケース、複数の重複ルールが競合するケースなど、あらゆるシナリオを想定したテストクラスを作成し、ロジックの堅牢性を担保します。特に、DmlOptionsを使用した際のSaveResultの解析ロジックは重点的にテストしてください。
  5. 明確なログ戦略: どのレコードが重複と判断され、どのような処理(マージ、削除、フラグ付けなど)が行われたのかを追跡できるように、カスタムオブジェクトやプラットフォームイベントに詳細なログを記録する仕組みを導入することを推奨します。

クリーンなデータは、Salesforceプラットフォームの価値を最大化するための基盤です。私たち開発者は、Apexという強力なツールを駆使して、その基盤を支える重要な役割を担っています。

コメント