Dependency Injection Sample for Apex Triggerを使って楽しくトリガを書こう

みなさん、こんにちは。本投稿では「Dependency Injection Sample for Apex Trigger」を利用して、Salesforce Platform上で楽しくApexトリガを書いていく方法をご紹介します。

Dependency Injection Sample for Apex Triggerとは

Apexトリガ を最も柔軟で汎用的に書くことを目指したオリジナルのApexフレームワークのサンプルコードです。複雑なApexトリガのコードを書くのに慣れているひとでも、どこにどのようにロジックを書いていけば将来の機能追加に耐えやすい設計となるのか?を考えることがあるかと思います。その問いに対する私からの1つの解が、こちらになります。
Apexコードを変更せず、カスタムメタデータ型レコードを変更するだけでどのApexコードを有効にするのかを操作できるようになっています。現在はサンプルコードのみを公開していますが、今後はテンプレートからオブジェクトごとに自動生成できるツールを展開予定です。乞うご期待。

インストール方法

GitHubに公開しているコードをSalesforce組織へデプロイすれば、すぐに動かすことができます。デプロイはSalesforce CLIとVS Codeを利用しますので、事前にセットアップしておいてください。
まずGitHubのリポジトリをクローンしてVS Codeで開きます。
(まだGitに習熟してないかたは、[Download ZIP]ボタンでダウンロード)

実行するコマンド



ダウンロードできたディレクトリを開くと、Salesforce DXスタイルの構造になっています。scratch組織へプッシュすることはもちろん、最新のVS Code拡張機能により、non-scratch組織、すなわちdeveloper組織やsandbox組織にもデプロイできます。どちらでも好きな方を選んでください。

scratch組織



developer組織

動作確認

デプロイができたら、サンプルコードの動作確認をします。取引先レコードを新規登録または更新時にApexトリガが起動することを確認しましょう。
  1. 新規登録時に、取引先名(Name)項目の先頭に[サンプル]という文字列を付ける。
  2. 新規登録または更新時に、Fax(FAX)項目に値が入っており、かつ電話(Phone)項目に値が入っていない場合は「FAXが入っている場合は電話番号も入力してください」というエラーにする。 

利用法① - トリガ実行タイミングを変更する

上記のロジックが実行されるタイミングを次の変更してみましょう。
  • 更新時に、取引先名(Name)項目の先頭に[サンプル]という文字列を付ける。

(1) Apexクラスを変更

「取引先名(Name)項目の先頭に[サンプル]という文字列を付ける」というロジックは、AccountTriggerService.cls に記載されている addPrefixToName メソッドに対応します。現在は、onBeforeInsert メソッドからのみ呼び出されていますので、onBeforeUpdate メソッドからも呼び出されるように変更します。これ以外、何も変更する必要がありません。Apexトリガや共通のApexトリガハンドラを変更する必要がありません。これがこのフレームワークの特徴です。

変更前

変更後

(2) カスタムメタデータ型のレコードを変更

AccountTriggerService.cls に対応するカスタムメタデータ型のレコードで「BEFORE UPDATE」のみがチェックされるように変更します。
動作確認してみましょう。新規登録時には何もなく、更新時に[サンプル]が追加されましたよね?

また、いずれのタイミングでも実行されたくないロジックは、Apexコードを変更することなく、「Active」のチェックを外して保存すればOKです。これも簡単ですね。

利用法② - 別のオブジェクトのトリガを追加する

サンプルコードにはAccountを利用していますが、別のオブジェクト、たとえばCaseに対してトリガを追加してみましょう。この方法をマスターすれば、様々な場面でこのフレームワークを利用できるようになるでしょう。
では、次のロジックを追加してください。
  • 新規登録時に、件名(Subject)項目の先頭に[テスト]という文字列を付ける。

(1) Apexトリガを追加

  1. CaseTrigger.trigger を作成し、AccountTrigger.trigger の内容をコピー
  2. 「Account」を「Case」に置換

(2) Apexクラスを追加

  1. CaseConstants.cls を作成し、AccountConstants.cls の内容をコピー
  2. 「Account」を「Case」に置換
  3. [テスト]という文字列用の定数 NAME_PREFIX_TEST を追加
  4. 不要なコードを削除

  1. CaseTriggerService.cls を作成し、AccountTriggerService.cls の内容をコピー
  2. 「Account」を「Case」に置換
  3. NAME_PREFIX_TEST を追加
  4. 件名(Subject)項目の先頭に[テスト]という文字列を付ける addPrefixToSubject メソッドを追加
  5. onBeforeInsert メソッドから addPrefixToSubject メソッドを呼び出し

(3) カスタムメタデータ型のレコードを追加

  1. Trigger_Observer.CaseTriggerService.md-meta.xml を作成し、Trigger_Observer.AccountTriggerService.md-meta.xml の内容をコピー
  2. 「Account」を「Case」に置換
  3. 「Before_Insert__c」を「true」、それ以外を「false」

(4) package.xml に資源を追加

  • ApexClass
    1. CaseConstants
    2. CaseTriggerService
  • ApexTrigger
    1. CaseTrigger
  • CustomMetadata
    1. Trigger_Observer.CaseTriggerService

こだわりポイント

  • オブジェクトごとに定数クラス(AccountConstants、CaseConstants)を分けています。CommonConstants にまとめても良いが、まとめることによる肥大化を避けたいためです。
  • Apexトリガ内には具体的なロジックを書きません。オブジェクトに関わらず統一的な順序で処理を実行したいためです。
  • Apexトリガのロジック用のクラス(AccountTriggerService、AccountTriggerValidation、CaseTriggerService)は必ず ITriggerObserver インターフェースを実装するルールとします。誰が書いてもロジックの記述場所を統一させたいためです。
  • AccountTriggerServiceとAccountTriggerValidationは同じクラスにまとめて書いても構いませんが、呼び出したいメソッド(addPrefixToName、ValidatePhone)は1つにまとめずに分けて書いてください。1つのものに複数の意味が含まれていると、カスタムメタデータ型の側から操作しにくくなります。
  • カスタムメタデータ型レコードの名前は、対応するApexクラス名と一致させることでわかりやすくします。
  • テストクラス実行のためだけのカスタムメタデータ型レコード CommonTriggerObserverMock を利用します。テストカバー率を向上させるためです。

さいごに

この書き方をマスターして、いままで以上の生産性を発揮していきましょう。
Happy Coding!!

コメント