[Apex] System.Requestクラスを使用したApexコンテキストの活用


はじめに

Salesforce Winter'21リリースの機能では動的フォーム動的アクションやレコードトリガフローなどが注目を浴び、ウェビナーなどでも多く紹介されました。Apex関連だとSafe Navigation Operatorが新機能として注目されたのではないかと思います。

そんな中ひっそりと身を潜めあまり目立たない新機能もちらほら。
しかし意外と使えそうな便利な機能もあったりします。
今回はそんな中から気になっていたApexのSystem.Requestクラスを触ってみました。
おそらくウェビナー等で紹介されることはほとんどなかったあまり知られていない新機能なのではないかと思います。

System.Requestクラスについて

RequestクラスはApex実行時のコンテキストを検出することができます。
コンテキストの詳細は後述しますが、例えば呼び出されたApexがVisualforceから呼ばれたのか、Apexバッチから呼ばれたのかなど、Apexが実行される起点となった機能を識別することができます。
またRequestクラスのオブジェクトは普遍的に一意のIDを保持しています。
すなわち実行される処理の1つ1つに個別のIDが付与されトランザクションを一意に識別することができます。

Requestクラスはとてもシンプルなクラスです。
定義されているメソッドは下記の3つです。

  • getCurrent メソッド

現在のRequestオブジェクトを返します。
このメソッドのみ静的メソッドになっています。
ここで得られたRequestオブジェクトを使用してコンテキストやリクエストIDを取得します。

    Request reqInfo = Request.getCurrent();
  • getQuiddity メソッド

RequestオブジェクトからSystem.Quiddity列挙型の値を返します。
この値を使って現在のApexのコンテキストを識別できます。

    Quiddity currentType = reqInfo.getQuiddity();

    switch on currentType {
        when VF {
            // Visualforceから呼び出された時の処理
        }   
        when AURA {
            // Auraコンポーネントから呼び出された時の処理
        }   
        when BATCH_APEX {
            // Apexバッチから呼び出された時の処理
        }   
        when else {
            // 上記以外の場合の処理
        }
    }        

Quiddity列挙型の定義はQuiddity Enumを参照してください。
様々なコンテキストが定義されています。

ちなみにApexトリガはQuiddity列挙型では定義されていません。
これはApexトリガが特定の機能のレコード操作を起点に動くものであり、Apexトリガ自体が処理の起点となることはないからだと思われます。レコードの標準画面の保存ボタンからレコード操作した場合にApexトリガ内で取得されるコンテキストは「SYNCHRONOUS」となり、アクションから更新した場合は「QUICK_ACTION」となりました。あくまで処理が実行される起点のコンテキストが設定されることに注意してください。Apexトリガのコンテキストを識別する場合はトリガコンテキスト変数で定義されるisExecuting変数を利用しましょう。

    if (Trigger.isExecuting) {
        // Apexトリガが実行されている場合の処理
    }
  • getRequestId メソッド

RequestオブジェクトからリクエストIDを返します。

    String currentRequestId = reqInfo.getRequestId();

取得される値は「4ZqyQhuTHWRXVOV2amni1-」のような形式の文字列です。
値は普遍的に一意の値が付与されるため、トランザクションを一意に識別することができます。
同一トランザクションの中であれば何度コールしても同じ値が取得されます。

Requestクラスの活用シーン

  • 共通化によるコード量の削減
    Apexコンテキストを検出できるようになったことで、コンテキストごとの処理を分岐させて実行することができます。 例えばほとんど同じ処理だが、呼び出し元の機能によって特定の部分の処理だけを分けたい場合に、2種類のメソッドを用意することで実現したケースがあったとします。

    // Visualforceから呼び出す処理
    public static void methodForVF() {        
        Account a = new Account();
        a.Name = 'テスト取引先';
        a.Description = '画面から作成';
        Insert a;        
    }

    // Apexバッチから呼び出す処理
    public static void methodForApexBatch() {        
        Account a = new Account();
        a.Name = 'テスト取引先';
        a.Description = 'バッチから作成';
        Insert a;                
    }

このように複数のメソッドで実装をすると、多くの処理が冗長になってしまう場合があります。
しかしRequestクラスから検出したコンテキストで条件分岐させれば、メソッド定義は1つで処理を共通化し、個別に処理が分かれる部分のみを条件分岐させることで、コード量を削減できる可能性があります。

    // 共通化した処理
    public static void methodForAll() {
        Account a = new Account();
        a.Name = 'テスト取引先';

        switch on Request.getCurrent().getQuiddity() {
            when VF {
                // Visualforceから呼び出された時の処理
                a.Description = '画面から作成';
            }   
            when BATCH_APEX {
                // Apexバッチから呼び出された時の処理
                a.Description = 'バッチから作成';
            }   
        }        
        Insert a;                
    }

今までもメソッドにパラメータを設定することで実現できていたケースもあります。
しかしRequestクラスを使用すればパラメータを設定せずともコンテキストごとの処理分岐が実装でき、呼び出し元に依存しないシンプルな実装をすることができます。また今までパラメータの受け渡しが難しいケース(例えば標準画面からの保存など)の場合も、簡単にコンテキストを識別することができるようになりました。

  • 非同期処理の連携への活用
    例えば
     Salesforce ⇒ 外部システム ⇒ Salesforce
    のようにSalesforceと外部システムの間で非同期に連携処理が実行される場合を考えます。 このようなケースにおいて同一タイミングで同一のレコード(SalesforceIDが同じ)の連携が発生する場合、SalesforceIDだけでは後続の処理でトランザクションを一意に特定できません。 例えば外部サービスでの処理を経て、Salesforceでトランザクションごとに集計するケースなどにおいては、単純にSalesforceIDを使ってしまうと正しく集計できませんし、トランザクションごとに連携(リクエスト)を分割するのもオーバーヘッドが大きくなります。 このような場合にトランザクションを特定する識別子としてRequestクラスのリクエストIDが活用できます。 リクエストIDは同一レコードでも、トランザクションが分かれると別のIDが付与されるため、外部システムを経由して戻ってきた場合もどちらの処理か特定することができます。

まとめ

今回はWinter'21でリリースされたSystem.Requestクラスについて活用シーンを考察してみました。
おそらく今までも既存の機能を使って同等のことはできていたかもしれません。
しかし実現はできてもコードがより複雑で大量になってしまっていたのではないかと思います。

2020年はローコード開発が注目を集めた年でした。SalesforceでもLightningアプリケーションビルダーやLightningフローで多くの機能進化がありました。おそらく今後もローコード開発に関わる機能アップデートは多くでてくることでしょう。
一方でApexなどのプロコード開発が今後完全になくなるわけではなく、今回のRequestクラスのようなプロコード開発に関わるアップデートも継続してリリースされており、着実に開発がしやすくなっていっています。
ローコード開発とプロコード開発のどちらか一方が優れているわけではなく、それぞれメリット・デメリットがあり、その特性をよく理解した上でユーザの要件や特性に合わせ最適なカスタマイズの方式を選択することが大切なのではないかと思います。

参考文献