Salesforce Apexクラスをマスターする:開発者向け完全ガイド

背景と応用シーン

Salesforceプラットフォームで働く私たちSalesforce 開発者 (Salesforce Developer) にとって、Apex (エイペックス) は最も強力なツールの一つです。Apexは、Salesforceのサーバで実行される、強く型付けされたオブジェクト指向のプログラミング言語です。標準の宣言的ツール(フローや入力規則など)だけでは実現不可能な、複雑なビジネスロジックを実装するために使用されます。その中核をなすのが Apex Class (Apexクラス) です。

Apexクラスは、再利用可能なコードの集合を定義するための設計図(ブループリント)です。一度定義すれば、さまざまな場所から呼び出して使用することができます。これにより、コードのモジュール化、保守性の向上、開発効率の向上が実現します。


主な応用シーン

Apexクラスの具体的な応用シーンは多岐にわたりますが、代表的なものとして以下が挙げられます。

  • カスタムビジネスロジックの実装: 標準機能では対応できない複雑な計算、レコードの自動作成・更新、特定の条件下での複数オブジェクトにまたがるデータ操作などを実装します。トリガーから呼び出されるハンドラクラスなどが典型例です。
  • Visualforceコントローラ: カスタムのVisualforceページに動的なデータやロジックを提供するためのバックエンドコントローラとして機能します。
  • Lightningコンポーネントのサーバーサイドコントローラ: Lightning Web Components (LWC) や Auraコンポーネントから呼び出され、データベースへのアクセスや複雑な処理を実行します。@AuraEnabled アノテーションが使用されます。
  • Webサービスの作成: 外部システムと連携するためのカスタム REST API (レスト エーピーアイ)SOAP API (ソープ エーピーアイ) を公開します。@RestResource@WebService アノテーションが使われます。
  • 非同期処理: 大量のデータを処理するための Batch Apex (バッチApex)、未来の特定の時間に処理を実行する Schedulable Apex (スケジュール可能Apex)、または非同期のコールアウトを行う Queueable Apex (キュー可能Apex) など、すべてクラスとして実装されます。
  • メールサービス: 受信メールをトリガーとして、Salesforce内でカスタムロジックを実行するメールサービスハンドラを実装します。

原理説明

Apexクラスの原理を理解するためには、Object-Oriented Programming (OOP, オブジェクト指向プログラミング) の基本的な概念を把握することが重要です。Apexクラスは、OOPの原則に基づいています。

クラスとオブジェクト

Class (クラス) は、オブジェクトを作成するための設計図です。例えば、「自動車」というクラスを定義した場合、そのクラスには「色」「メーカー」「最高速度」といったプロパティ (Properties) や、「走る」「止まる」「曲がる」といったメソッド (Methods) が含まれます。

一方、Object (オブジェクト) は、そのクラスから作成された具体的なインスタンスです。「赤いトヨタのプリウス」や「青いホンダのシビック」は、それぞれ「自動車」クラスの具体的なオブジェクトです。

クラスの構成要素

Apexクラスは主に以下の要素で構成されます。

  • アクセス修飾子 (Access Modifiers): private, public, global といったキーワードで、クラスやそのメンバーがどこからアクセス可能かを定義します。public は同じ名前空間内から、global は名前空間を超えてアクセス可能です。
  • 共有キーワード (Sharing Keywords): with sharing, without sharing, inherited sharing があり、クラスが実行される際のレコード共有ルールを制御します。これはSalesforceのデータセキュリティにおいて非常に重要な概念です。
  • 変数 (Variables): データを保持するためのメンバー変数です。インスタンス変数や静的(static)変数があります。
  • メソッド (Methods): 特定の処理を実行するコードのブロックです。オブジェクトの振る舞いを定義します。
  • コンストラクタ (Constructors): クラスからオブジェクトをインスタンス化する際に呼び出される特殊なメソッドです。オブジェクトの初期化処理を行います。

これらの要素を組み合わせて、特定の機能を持つ独立したコンポーネントとしてApexクラスを作成します。これにより、コードの再利用性が高まり、複雑なアプリケーションを整理された構造で構築することが可能になります。

示例コード

ここでは、Salesforceの公式ドキュメント「Apex Developer Guide」に記載されている例を基に、新しい取引先レコードを作成するシンプルなユーティリティクラスを作成します。このようなユーティリティクラスは、さまざまな場所から共通のロジックを呼び出す際に非常に便利です。

このクラスは、取引先名を受け取り、新しい取引先レコードをデータベースに挿入する静的メソッド(static method)を持っています。

// publicキーワードにより、このクラスはアプリケーションのどこからでもアクセス可能です。
public class AccountUtils {

    // public staticキーワードにより、このメソッドはクラスのインスタンスを作成することなく、
    // AccountUtils.createAccount('My New Account'); のように直接呼び出すことができます。
    // String型の 'name' を引数として受け取ります。
    // 戻り値は作成されたAccountオブジェクトです。
    public static Account createAccount(String name) {
        
        // 新しいAccountオブジェクトのインスタンスを作成し、
        // Name項目に引数で受け取った値を設定します。
        Account newAcct = new Account(Name = name);
        
        // try-catchブロックを使用して、データベース操作(DML)中に発生する可能性のある
        // エラーを適切に処理します。これは例外処理のベストプラクティスです。
        try {
            // DML (Data Manipulation Language) の insert 操作を実行して、
            // 新しい取引先レコードをデータベースに保存します。
            insert newAcct;

        } catch (DmlException e) {
            // DmlExceptionは、insert, update, deleteなどのDML操作に失敗した場合にスローされます。
            // 例えば、必須項目が設定されていない、入力規則に違反した場合などです。
            // エラーメッセージをデバッグログに出力します。
            System.debug('A DML exception has occurred: ' + e.getMessage());
            
            // エラーが発生した場合は、nullを返して呼び出し元に失敗を伝えます。
            return null;
        }
        
        // DML操作が成功した場合、作成されたAccountオブジェクト(IDが採番されている)を返します。
        return newAcct;
    }
}

このクラスを使用するには、例えば匿名実行ウィンドウで次のようにコードを実行します。

// AccountUtilsクラスのcreateAccount静的メソッドを呼び出します。
Account myAccount = AccountUtils.createAccount('Test Account From Apex');

// 戻り値がnullでないか(作成が成功したか)を確認します。
if (myAccount != null) {
    // 成功した場合、作成された取引先のIDをデバッグログに出力します。
    System.debug('Account created successfully with ID: ' + myAccount.Id);
} else {
    // 失敗した場合のメッセージをログに出力します。
    System.debug('Failed to create account.');
}

注意事項

Apexクラスを開発する際には、Salesforceプラットフォーム特有の制約や考慮事項を常に意識する必要があります。

Governor Limits (ガバナ制限)

Salesforceはマルチテナント環境であるため、すべてのユーザーが安定してリソースを利用できるよう、1回のトランザクションで実行できる処理には厳格な制限(ガバナ制限)が設けられています。

  • SOQLクエリ: 1トランザクションあたり100回まで
  • DMLステートメント: 1トランザクションあたり150回まで
  • CPU時間: 10,000ミリ秒まで
これらの制限を超えると、コードは例外をスローして実行を停止します。したがって、常に効率的なコードを書く必要があります。

Bulkification (一括処理)

ガバナ制限を回避するための最も重要な設計パターンが「一括処理(Bulkification)」です。これは、コードが単一のレコードだけでなく、複数のレコード(最大200レコードのリスト)を一度に処理できるように設計することを意味します。特に、ループの中でSOQLクエリやDMLステートメントを実行することは絶対に避けるべきです。

セキュリティと共有ルール

クラス定義時に with sharing または without sharing を明示的に指定することが推奨されます。

  • with sharing: クラスを実行するユーザーの共有ルールが適用されます。ユーザーがアクセス権を持たないレコードは、コード内でも参照・更新できません。
  • without sharing: 共有ルールを無視して、すべてのレコードにアクセスできます。システムレベルの処理で使われますが、権限昇格のリスクがあるため慎重に使用する必要があります。
指定がない場合、呼び出し元のコンテキストに依存するため、意図しない挙動を招く可能性があります。

例外処理 (Exception Handling)

DML操作やSOQLクエリは、さまざまな理由で失敗する可能性があります。try-catch ブロックを使用して、これらの例外を適切に捕捉し、ユーザーフレンドリーなエラーメッセージを表示したり、代替処理を行ったりすることが重要です。

Test Coverage (テストカバレッジ)

本番環境にApexクラスをデプロイするには、そのクラスのコード行の少なくとも75%がテストクラスによって実行される必要があります。これは単なる数値目標ではなく、コードの品質を保証するための重要なプロセスです。ポジティブシナリオ、ネガティブシナリオ、一括処理シナリオを網羅した、堅牢なテストクラスを作成することが不可欠です。


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

Apexクラスは、Salesforceプラットフォームの能力を最大限に引き出すための鍵となる機能です。宣言的なツールでは実現できない要件に応え、ビジネスプロセスを自動化し、外部システムとのシームレスな統合を可能にします。

効果的で保守性の高いApexクラスを作成するために、以下のベストプラクティスを心がけましょう。

  1. 単一責任の原則: 1つのクラスは、1つの明確な責務(例えば、取引先関連のユーティリティ、特定のビジネスロジックの実行など)を持つように設計します。これにより、コードの可読性と再利用性が向上します。
  2. 命名規則の遵守: クラス名、メソッド名、変数名は、その目的が明確にわかるような命名規則に従います。例えば、クラス名は AccountService、メソッド名は calculateDiscounts のようにします。
  3. ガバナ制限を常に意識する: 特にループ内でのデータベースアクセスは避け、コレクション(List, Set, Map)を最大限に活用してコードを一括処理に対応させます。
  4. ハードコーディングを避ける: レコードIDや特定のURLなどをコード内に直接書き込むのではなく、カスタムメタデータ型、カスタム設定、またはカスタム表示ラベルを使用して、設定を外部化します。
  5. 適切なコメントを追加する: 複雑なロジックや、なぜそのように実装したのかという意図をコメントとして残すことで、将来の自分や他の開発者がコードを理解しやすくなります。
  6. トリガーロジックの分離: トリガー内には直接ロジックを書かず、ハンドラクラスを呼び出すだけにします。これにより、ロジックの管理、再利用、テストが容易になります。

これらの原則とプラクティスを遵守することで、私たちはSalesforce開発者として、スケーラブルで堅牢、かつ保守性の高い優れたソリューションを構築することができるのです。

コメント