Salesforce Flow から Apex を呼び出す:開発者向けガイド

背景と応用シナリオ

Salesforce の自動化ツールの中でも、Flow (フロー) はその視覚的なインターフェースと強力な機能により、Salesforce 管理者と開発者の双方にとって不可欠なツールとなっています。クリック操作でビジネスプロセスを自動化できる一方で、標準機能だけでは対応しきれない複雑な要件に直面することもあります。

例えば、以下のようなシナリオが考えられます。

  • 外部システムの API を呼び出し、取得したデータで Salesforce レコードを更新する必要がある。
  • 複雑な数学的計算や、JSON データの解析・生成など、標準の数式や要素では実現が困難なロジックを実装したい。
  • 複数の関連オブジェクトにまたがるレコードを、特定のビジネスルールに基づいて一括で処理(DML 操作)する必要があるが、Flow のループ処理ではガバナ制限に抵触するリスクがある。
  • Apex でしか利用できない特定の機能(例:暗号化クラス、一部のメタデータ操作など)を活用したい。

このような時、Flow の能力を拡張する最も強力な手段が Apex (エイペックス) の呼び出しです。Flow を「司令塔」とし、複雑な処理を Apex に「委任」することで、宣言的なツールの利便性と、プログラミング言語の柔軟性・パワーを両立させることができます。本記事では、Salesforce 開発者の視点から、Flow から Apex アクションを呼び出すための原理、具体的な実装方法、そしてベストプラクティスについて詳しく解説します。


原理説明

Flow から Apex コードを呼び出すための主要な仕組みは、Invocable Method (呼び出し可能なメソッド) と呼ばれる特別なアノテーションを利用することです。開発者は、特定のルールに従って Apex クラスとメソッドを定義することで、そのロジックを Flow Builder 内で一つの「アクション」要素として利用可能にすることができます。

1. @InvocableMethod アノテーション

Flow から呼び出されるメソッドには、@InvocableMethod アノテーションを付与する必要があります。このアノテーションが、Salesforce プラットフォームに対して「このメソッドは Flow や他の宣言的ツールから呼び出し可能です」と伝える役割を果たします。

このアノテーションには、いくつかの重要なプロパティがあります。

  • label: Flow Builder のアクション選択画面に表示される名前です。管理者が理解しやすい、直感的な名前を付けることが重要です。(例: label='取引先に関連する取引先責任者の役職を更新'
  • description: アクションの詳細な説明です。どのような処理を行うのか、必要な入力は何かを記述します。
  • category: Flow Builder 内でアクションを分類するためのカテゴリ名です。(例: category='Account Management'
  • callout: メソッドが外部 API へのコールアウトを行う場合に true に設定します。これを true にすると、トランザクションの制御が変わり、コールアウト前の DML 操作が自動的にコミットされます。

重要な制約として、1 つの Apex クラスには @InvocableMethod アノテーションを持つメソッドを 1 つしか定義できません。

2. @InvocableVariable アノテーション

Flow から Apex へデータを渡す、または Apex から Flow へデータを返すためには、@InvocableVariable アノテーションを使用します。このアノテーションは、クラスのメンバ変数に付与します。

  • label: Flow の入力・出力設定画面で表示される変数名です。
  • description: 変数の説明です。
  • required: この変数が入力として必須かどうかを true または false で指定します。

Flow と Apex 間でデータをやり取りする場合、直接プリミティブ型(String, Integer など)や sObject を渡すのではなく、これらの変数を内包するカスタムのラッパークラスを定義するのが一般的です。

3. データ構造と一括処理(Bulkification)

Flow と Apex の連携において最も重要な概念の一つが一括処理 (Bulkification) です。Flow がループ内で Apex アクションを呼び出す場合でも、Salesforce はパフォーマンスを最適化し、ガバナ制限を回避するために、複数の呼び出しを 1 つのトランザクションにまとめて Apex メソッドに渡します。

このため、@InvocableMethod で定義されたメソッドは、常に入力として List を受け取り、出力として List を返す必要があります。たとえ Flow 上で 1 つのレコードだけを処理する想定であっても、Apex 側は複数のリクエストを同時に処理できるように設計しなければなりません。

例えば、メソッドのシグネチャは以下のようになります。 public static List<OutputClass> myMethod(List<InputClass> inputs)

この設計により、Apex コードは自然とガバナ制限(特に SOQL クエリや DML ステートメントの発行回数)に準拠しやすくなります。


サンプルコード

ここでは、Salesforce の公式ドキュメントに記載されている例を基に、指定された取引先 ID のリストを受け取り、それぞれの取引先名を返すシンプルな Apex アクションを作成します。

このコードは、Flow から取引先 ID のリスト(例えば、コレクション変数)を渡し、結果として取引先名のリストを受け取って、画面に表示したり、他のレコードの更新に使用したりするシナリオで利用できます。

public class AccountManager {
  // InvocableMethod アノテーションを付与し、Flow から呼び出し可能にする
  @InvocableMethod(label='Get Account Names' description='Returns the list of account names for the given account IDs.' category='Account')
  public static List<String> getAccountNames(List<ID> ids) {
    // 結果を格納するための String 型のリストを初期化
    List<String> accountNames = new List<String>();

    // 入力として受け取った ID のリストを基に、取引先を検索
    // SOQL クエリは for ループの外に配置し、一括処理の原則に従う
    List<Account> accounts = [SELECT Name FROM Account WHERE Id IN :ids];

    // 取得した取引先リストをループ処理
    for (Account account : accounts) {
      // 各取引先の名前を結果リストに追加
      accountNames.add(account.Name);
    }
    
    // 処理結果のリストを Flow に返す
    return accountNames;
  }
}

コードの解説:

  • L3: @InvocableMethod - このメソッドが Flow から呼び出し可能であることを宣言しています。labeldescriptioncategory を指定することで、Flow Builder での利便性が向上します。
  • L4: public static List<String> getAccountNames(List<ID> ids) - メソッドのシグネチャです。
    • public static: 呼び出し可能なメソッドは、常に public static である必要があります。
    • List<String>: 戻り値は List 型でなければなりません。この例では、取引先名の文字列リストを返します。
    • List<ID> ids: 入力パラメータも List 型でなければなりません。この例では、取引先 ID のリストを受け取ります。
  • L9: [SELECT Name FROM Account WHERE Id IN :ids] - これが Bulkification の核心です。入力されたすべての ID を使って、1 回の SOQL クエリで必要なデータをすべて取得します。ループ内で SOQL を実行するアンチパターンを回避しています。
  • L12-L14: 取得した結果をループで処理し、戻り値用のリストを作成します。
  • L17: return accountNames; - 最終的な結果を Flow に返却します。このリストの各要素が、Flow 内の出力変数にマッピングされます。

注意事項

Flow から Apex を呼び出す際には、いくつかの重要な点に注意する必要があります。これらを怠ると、予期せぬエラーやパフォーマンスの低下、セキュリティリスクにつながる可能性があります。

権限 (Permissions)

Flow を実行するユーザーは、呼び出される Apex クラスへのアクセス権を持っている必要があります。プロファイルまたは権限セットで、対象の Apex クラスへのアクセスを有効にしてください。これを忘れると、権限のないユーザーが Flow を実行した際にエラーが発生します。また、Apex クラスの共有キーワード(with sharing, without sharing, inherited sharing)が、レコードへのアクセス可視性を決定します。Flow が実行されるコンテキスト(ユーザコンテキストなど)と、Apex の共有設定の組み合わせによって、データへのアクセス範囲が変わることを常に意識してください。

API 制限 (Governor Limits)

呼び出された Apex コードは、Salesforce の標準的な Governor Limits (ガバナ制限) の対象となります。これには、トランザクションあたりの SOQL クエリ発行回数(100回)、DML ステートメント発行回数(150回)、合計 CPU 時間(10,000ミリ秒)などが含まれます。前述の通り、Apex コードは必ず一括処理を前提として設計し、ループ内での SOQL や DML を避けるなど、ベストプラクティスを徹底してください。

エラー処理 (Error Handling)

Apex コード内で例外が発生した場合、その例外は Flow に伝播し、Flow は失敗します。堅牢な自動化を構築するためには、Apex 側で try-catch ブロックを使用して例外を捕捉し、適切に処理することが不可欠です。

例えば、エラーメッセージをラップして Flow に返すことで、Screen Flow でユーザーに分かりやすいエラーを表示したり、エラー内容に基づいて後続の処理を分岐させたりすることが可能です。Flow Builder 側でも、アクション要素にフォルトパス (Fault Path) を設定し、Apex アクションが失敗した場合の代替処理(エラーログの作成、管理者に通知など)を定義することが強く推奨されます。

コールアウト (Callouts)

Apex アクションから外部システムの API を呼び出す場合(コールアウト)、メソッドに @InvocableMethod(callout=true) を指定する必要があります。これにより、Salesforce はトランザクションを分割して処理します。コールアウトを含むトランザクションでは、コールアウトを実行する前に未コミットの DML 操作を行うことはできません。もし DML が必要な場合は、コールアウトの前にトランザクションを完了させるか、コールアウト後の別のトランザクションで DML を実行する設計が必要です。


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

Flow と Apex の連携は、Salesforce プラットフォームの宣言的機能とプログラム的機能の長所を組み合わせるための、非常に強力なパラダイムです。開発者はこの機能を活用することで、管理者が構築した Flow の能力を飛躍的に高めることができます。

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

  1. 宣言的なアプローチを第一に (Declarative First): まずは Flow の標準機能で要件を満たせないか検討しましょう。Apex は、標準機能では不可能な、あるいは非効率な処理を補うための「最後の手段」として用いるのが理想です。
  2. 常に一括処理を意識する (Bulkify Your Apex): Invocable Method は常に List を受け取り、List を返すように設計します。これにより、将来的なデータ量の増加にも耐えうる、スケーラブルな自動化が実現できます。
  3. 汎用性と再利用性を高める (Generic and Reusable): 特定の ID や値をハードコーディングするのではなく、Flow からの入力変数として受け取るように設計しましょう。これにより、作成した Apex アクションを複数の異なる Flow やビジネスプロセスで再利用できます。
  4. 明確な命名と説明 (Clear Naming and Description): labeldescription アノテーションを適切に設定し、Flow を作成する管理者や他の開発者が、そのアクションの目的、必要な入力、期待される出力を簡単に理解できるようにしましょう。これはチーム開発における重要なドキュメンテーションの一部です。
  5. 堅牢なエラーハンドリング (Robust Error Handling): Apex 側の try-catch と Flow 側のフォルトパスを組み合わせ、予期せぬ事態にも対応できる回復力の高い自動化プロセスを構築してください。

これらの原則に従うことで、Flow と Apex の連携を最大限に活用し、複雑で価値の高いビジネス要件を、効率的かつ安定的に実現することができるでしょう。

コメント