Skip to main content

実行コンテキストの理解

学習の目的

この単元を完了すると、次のことができるようになります。

  • Apex の呼び出しに使用する方法を理解する
  • Salesforce オブジェクトのトリガーを記述する
  • 開発者コンソールでコードを実行して実行コンテキストの動作を観察する
  • ガバナ制限が設計パターンに与える影響を理解する
  • 一括操作を使用することの重要性を理解する
メモ

メモ

このバッジのハンズオン Challenge は日本語、スペイン語 (LATAM)、ポルトガル語 (ブラジル) に対応しています。Playground の言語を変更するには、こちらの指示に従ってください。日本語等、翻訳された言語と英語に差異がある可能性があります。英語以外の言語での指示に従って Challenge に合格できなかった場合は、[言語] と [地域] をそれぞれ [English]、[アメリカ合衆国] に切り替えてからもう一度お試しください。

翻訳版 Trailhead を活用する方法の詳細は、自分の言語の Trailhead バッジを参照してください。

一緒にトレイルを進みましょう

エキスパートの説明を見ながらこのステップを実行したい場合は、次の動画をご覧ください。これは「Trail Together」(一緒にトレイル) シリーズの一部です。

(巻き戻して最初から見直したい場合、このクリップは 16:36 分から開始されます。)

実行コンテキストとは?

ASP.NET アプリケーションの場合、コードはアプリケーションドメインのコンテキストで実行されます。Lightning プラットフォーム環境では、コードは実行コンテキスト内で実行されます。端的に言うと、このコンテキストはコードが実行されてから終了するまでの時間を表します。重要なのは、作成する Apex コード以外のコードが実行される可能性があると理解しておくことです。

このしくみを理解するには、プラットフォームで Apex コードを実行できる方法をすべて知っておく必要があります。

Apex を呼び出す方法

方法 説明
データベーストリガー カスタムまたは標準オブジェクトでの特定のイベントに対して呼び出されます。
匿名 Apex 開発者コンソールやその他のツールにおいてその場で実行されるコードスニペット。
非同期 Apex future または Queueable Apex の実行時、一括処理ジョブの実行時、または指定間隔での Apex 実行のスケジュール時に行われます。
Web サービス SOAP または REST Web サービスを介して公開されるコード。
メールサービス 受信メールを処理するために設定されるコード。
Visualforce または Lightning ページ Visualforce コントローラーおよび Lightning コンポーネントは、自動的に、またはユーザーがアクションを開始したとき (ボタンのクリックなど) に Apex コードを実行できます。Lightning コンポーネントも Lightning プロセスおよびフローによって実行できます。

Apex コードの呼び出し以外にも、新規 ToDo の作成、メールの送信、項目自動更新の実行、アウトバウンドメッセージの送信などのアクションすべてを、いずれかの宣言型プラットフォーム機能によってトリガーできます。これらのアクションも、実行コンテキスト内で実行されます。

もう 1 つの重要な考慮事項は、Apex コードを実行するユーザーのコンテキストです。デフォルトでは、Apex はシステムコンテキストで実行されます。Apex コードはすべてのオブジェクトおよび項目にアクセスできます。オブジェクト権限、項目レベルセキュリティ、共有ルールは現在のユーザーには適用されません。with sharing キーワードを使用して、クラスで現在のユーザーの共有ルールを考慮するように指定できます。このトピックは重要であるため、詳細は、「with sharing または without sharing キーワードの使用」を参照してください。

トリガーの基本

実行コンテキストについて詳しく学習する前に、少し立ち止まってデータベーストリガーについて説明しましょう。SQL Server のトリガーと同様に、Apex データベーストリガーは Salesforce のレコードに対するイベントの前または後にプログラミングロジックを実行します。トリガーを定義するときに、次のイベントの 1 つ以上を指定できます。

  • before insert
  • before update
  • before delete
  • after insert
  • after update
  • after delete
  • after undelete

トリガーの基本構文は次のようになります。

trigger TriggerName on ObjectName (trigger_events) {
   // code_block
}

.NET 開発者がコードで問題を解決するのが好きなことはわかっていますが、ここでは生産性を飛躍的に高めるヒントを紹介します。トリガーは、同じ処理を Salesforce が提供するポイント & クリック自動化ツールのいずれでも実行できないことが絶対確実である場合にのみ、最後の手段として使用します。

効率的に作業を進めるために、Salesforce プラットフォームには、コードを記述せずにビジネスロジックを管理するための強力な自動化ツール Flow Builder が用意されています。ほとんどの場合、かつてはトリガーでしか実行できなかったタスクに、より適した自動化ツールがあります。

メモ

メモ

初めてこのプラットフォームを使用する場合は、トリガーを作成する前に、必ず時間を取って開発者向け初級トレイルの「承認プロセスを使用してレコードを承認する」モジュールを参照してください。Salesforce 組織に多大かつ不要な技術的作業負担をもたらした張本人になることを避けましょう。

実行コンテキストのマーク

実行コンテキストの理解を深めるために、新規取引先が入力されたら商談を作成する Apex データベーストリガーを手順に従って作成してみましょう。このトリガーはハンドラークラスからメソッドをコールするため、まずハンドラークラスを作成します。

  1. [設定] で、[<あなたの名前>]あなたの名前> > [開発者コンソール] をクリックして開発者コンソールを開きます。
  2. 開発者コンソールで、[File (ファイル)] > [New (新規)] > [Apex Class (Apex クラス)] を選択します。
  3. クラス名として「AccountHandler」と入力し、[OK] をクリックします。
  4. 既存のコードを削除して、次のスニペットを挿入します。
    public with sharing class AccountHandler {
        public static void CreateNewOpportunity(List<Account> accts) {
            for (Account a :accts) {
                Opportunity opp = new Opportunity();
                opp.Name = a.Name + ' Opportunity';
                opp.AccountId = a.Id;
                opp.StageName = 'Prospecting';
                opp.CloseDate = System.Today().addMonths(1);
                insert opp;
            }
        }
    }
    
  5. Ctrl + S キーを押してクラスを保存します。
メモ

メモ

次の例のように、オブジェクトごとにトリガーを 1 つのみ使用するのがベストプラクティスと考えられています。そうすることで、トリガー内でコンテキスト固有のハンドラーメソッドを使用してロジックのないトリガーを作成できます。この方法を採用することで、新しい開発者がよく陥る落とし穴を避けることができます。これらの技法についての詳細は、「Trigger Frameworks and Apex Trigger Best Practices (トリガーのフレームワークと Apex トリガーのベストプラクティス)」を参照してください。

ハンドラークラスができたので、取引先トリガーを作成します。

  1. 開発者コンソールで、[File (ファイル)] > [New (新規)] > [Apex Trigger (Apex トリガー)] を選択します。
  2. 名前に「AccountTrigger」と入力し、sObject として [Account (取引先)] を選択します。
  3. [送信] をクリックします。
  4. 既存のコードを削除して、次のスニペットを挿入します。
    trigger AccountTrigger on Account (before insert, before update, before
        delete, after insert, after update, after delete,  after undelete) {
        if (Trigger.isAfter && Trigger.isInsert) {
            AccountHandler.CreateNewOpportunity(Trigger.New);
        }
    }
    
  5. Ctrl + S キーを押してトリガーを保存します。

手順の最後に、Salesforce インターフェースを使用して新規取引先を入力するユーザーをシミュレートする匿名コードを実行します。Apex コードを実行するにはさまざまな方法があります。

  1. [設定] で、[<あなたの名前>]あなたの名前> > [開発者コンソール] をクリックして開発者コンソールを開きます。
  2. [Debug (デバッグ)] > [Open Execute Anonymous Window (実行匿名ウィンドウを開く)] を選択します。
  3. 既存のコードを削除して、次のスニペットを挿入します。
    Account acct = new Account(
        Name='Test Account 2',
        Phone='(415)555-8989',
        NumberOfEmployees=50,
        BillingCity='San Francisco');
    insert acct;
    
  4. [Open Log (ログを開く)] オプションが選択されていることを確認し、[Execute (実行)] をクリックします。新しいタブに実行ログが表示されます。実行ログを入念に調査できるように開いたままにします。

実行ログの調査

実行ログの最初の行には EXECUTION_STARTED イベント、最後の行には EXECUTION_FINISHED イベントがマークされています。その間にあるすべてが実行コンテキストです。

では、何が起こったかを詳しく見ていきましょう。CODE_UNIT_STARTED イベントは、[Execute Anonymous (実行匿名)] ウィンドウからコードが起動されたときにマークされます。下図では、この行は赤で強調表示されています。

開発者コンソールのデバッグログのスクリーンショット。CODE_UNIT_STARTED イベントが強調表示されています。

強調表示された 2 つ目の CODE_UNIT_STARTED 行は、BeforeInsert イベントのコードが実行されたときを表します。

この図には表示されていませんが、開発者コンソールの独自のインスタンスを使用している場合は、結果をさらにスクロールダウンして CODE_UNIT_STARTED の他のインスタンスを探してください。少なくとももう 1 つ、AfterInsert イベントのコードが実行されたときを表すインスタンスが表示されるはずです。新規取引先が作成されたら起動するワークフロールールを作成した場合は、それらも実行ログに表示されます。このコードはすべて同じ実行コンテキスト下で動作するため、同じガバナ制限セットが適用されます。

なぜガバナ制限を理解するのがそれほど重要なのでしょうか? Salesforce はマルチテナント環境であるため、1 つの Salesforce 組織の各インスタンスがリソースを消費しすぎないようにこうしたガバナ制限が不可欠です。基本的に、ガバナ制限はシステム全体がクラッシュしないようにするものです。

制限への対応

再び、制限への対応というテーマに戻ってきました。おそらく最も考慮することになる 2 つの制限には、SOQL クエリまたは DML ステートメントの数が関係します。このプラットフォームに不慣れな開発者はこれらの制限に足をすくわれがちなので、少し余分に時間をとって回避方法に取り組んでください。

メモ

メモ

意識すべき制限は数多くあり、それらはメジャーリリースごとに変化する傾向にあります。さらに、制限が厳しくならずに緩くなることもよくあるため、必ず「リソース」にある「実行ガバナと制限」リンクで最新の制限を確認してください。

一括処理

多くの開発者が、コードを 1 件のレコードを処理するように設計するという共通の罠に落ちています。Lightning プラットフォームではこれは大きな間違いであることにすぐ気付くでしょう。

Apex トリガーが一度に受信できるレコードは最大 200 個です。現在、SOQL クエリの合計数の同期制限は 100、発行される DML ステートメントの合計数の同期制限は 150 です。そのため、ループ内で SOQL クエリまたは DML ステートメントを実行するトリガーがあり、そのトリガーが一括処理で起動されたらどうなるでしょうか?

そうです!

制限エラーになります。コードをリリースしてしばらくの間、つまり制限に達するまでは正常に実行できる可能性があります。ひとたび制限に達したら、開発者はすぐにコードを調べて「一括処理対応」方法を解明しなければなりません。この設計し直しに最初の設計よりも時間がかかることがよくあります。この状況を避けるために、最初から Apex コードを一括処理に対応できるように設計します。この方法は、「一括 Apex トリガー」モジュールで学習できます。

お気づきかもしれませんが、前に作成したトリガーハンドラーコードでは一括パターンを使用していなかったため、制限エラーになる可能性があります。もう一度、元のコードを確認してみましょう。

public with sharing class AccountHandler {
    public static void CreateNewOpportunity(List<Account> accts) {
        for (Account a :accts) {
            Opportunity opp = new Opportunity();
            opp.Name = a.Name + ' Opportunity';
            opp.AccountId = a.Id;
            opp.StageName = 'Prospecting';
            opp.CloseDate = System.Today().addMonths(1);
            insert opp;
        }
    }
}

insert DML 操作が for ループ内にあります。これは、非常によくない方法ですので、常に避けてください。

幸い、1 ステップでループ内のリスト変数に書き出し、リストの内容を挿入するようにコードを変更して修正できます。

  1. [設定] で、[<あなたの名前>]あなたの名前> > [開発者コンソール] をクリックして開発者コンソールを開きます。
  2. 開発者コンソールで [File (ファイル)] > [Open (開く)] を選択します。
  3. エンティティ種別として、[Classes (クラス)] を選択します。エンティティとして [AccountHandler] を選択します。
  4. [Open (開く)] をクリックします。
  5. 既存のコードを削除して、次のスニペットを挿入します。
    public with sharing class AccountHandler {
        public static void CreateNewOpportunity(List<Account> accts) {
            List<Opportunity> opps = new List<Opportunity>();
            for (Account a :accts) {
                Opportunity opp = new Opportunity();
                opp.Name = a.Name + ' Opportunity';
                opp.AccountId = a.Id;
                opp.StageName = 'Prospecting';
                opp.CloseDate = System.Today().addMonths(1);
                opps.add(opp);
            }
            if (opps.size() > 0) {
                insert opps;
            }
        }
    }
    
  6. Ctrl + S キーを押してクラスを保存します。

トリガーハンドラーコードを修正できたので、テストしてトリガーが 200 レコードという負荷を処理できることを確認しましょう。ご存じのように、ベストプラクティスとして、コードが機能することを確認できるように単体テストを記述します。

  1. 開発者コンソールで、[File (ファイル)] > [New (新規)] > [Apex Class (Apex クラス)] を選択します。
  2. クラス名として「AccountTrigger_Test」と入力し、[OK] をクリックします。
  3. 既存のコードを削除して、次のスニペットを挿入します。
    @isTest
    private class AccountTrigger_Test {
        @isTest static void TestCreateNewAccountInBulk() {
            // Test Setup data
            // Create 200 new Accounts
            List<Account> accts = new List<Account>();
            for(Integer i=0; i < 200; i++) {
                Account acct = new Account(Name='Test Account ' + i);
                accts.add(acct);
            }              
            // Perform Test
            Test.startTest();
            insert accts;                               
            Test.stopTest();
            // Verify that 200 new Accounts were inserted
            List<Account> verifyAccts = [SELECT Id FROM Account];
            System.assertEquals(200, verifyAccts.size());    
            // Also verify that 200 new Opportunities were inserted
            List<Opportunity> verifyOpps = [SELECT Id FROM Opportunity];                              
            System.assertEquals(200, verifyOpps.size());                             
        }
    }
    
  4. Ctrl + S キーを押してクラスを保存します。
  5. [Test (テスト)] > [New Run (新規実行)] を選択します。
  6. TestClass として AccountTrigger_Test、テストメソッドとして TestCreateNewAccountInBulk を選択します。
  7. [実行] をクリックします。
  8. [Test (テスト)] タブを選択し、テストがエラーなしで完了したことを確認します。これは、[Status (状況)] 列の緑のチェックマークで示されます。
メモ

メモ

単体テストについてはまだ説明していませんが、心配はいりません。いくつか異なるキーワードがあるだけで、Lightning プラットフォームでも .NET と同様に動作します。その構造についてはすぐに習熟できます。Apex トリガーのテストについての詳細は、「リソース」のリンクを参照してください。

もうひとこと...

Apex では、お馴染みの try-catch-finally ブロックを使用して例外を処理します。ただし、Apex コードが実行される場所に応じて、catch ステートメントとおそらく rollback も変わる可能性があります。Apex で try、catch、rollback を処理する際のベストプラクティスについては、「リソース」のリンクを参照してください。

Lightning プラットフォームにはアプリケーション変数やセッション変数のようなものはありません。クラス間でデータを保持する必要がある場合は、静的変数を使用します。ただし、静的変数の動作は Lightning プラットフォームと .NET では異なることに留意してください。Lightning プラットフォーム環境では、静的変数が情報を保持できるのは 1 つの実行コンテキスト内に限定されますが、他のオプションを使用してトリガー呼び出し間でデータを保持することはできます。詳細は、「リソース」の高度な Apex に関するリンクを参照してください。

制限に対応する場合、特に管理パッケージの開発者は、多くのトレードオフを考慮する必要があります。Salesforce パートナーは通常、管理パッケージを使用してアプリケーションを配布および販売します。このモジュールでは、知っておくべき情報の概要をざっと確認しただけです。Apex 開発に本格的に取り組む場合は、「リソース」の高度な Apex に関する リンクを参照してください。

リソース

無料で学習を続けましょう!
続けるにはアカウントにサインアップしてください。
サインアップすると次のような機能が利用できるようになります。
  • 各自のキャリア目標に合わせてパーソナライズされたおすすめが表示される
  • ハンズオン Challenge やテストでスキルを練習できる
  • 進捗状況を追跡して上司と共有できる
  • メンターやキャリアチャンスと繋がることができる