Apexトリガー

ApexトリガーはSalesforce開発の中核ですが、同時に最も事故が多い領域です。
理由はシンプルで、「適当に書いても一応動く」からです。

しかし実務ではそれが致命傷になります。

トリガーの本質

トリガーは「イベント駆動」です。

発火タイミング:
・before insert
・after insert
・before update
・after update
・before delete
・after delete

ここでまず重要な理解:

👉 before = 値変更用 / after = 関連処理用

アンチパターン(これやると終わる)

① トリガーに全部書く

trigger BadTrigger on Account (before insert) {
    for (Account acc : Trigger.new) {
        // ロジック全部ここ
    }
}

問題:
・再利用不可
・テストしにくい
・肥大化する

② トリガー複数作成

→ 実行順序が保証されない
→ バグの温床

👉 原則:1オブジェクト1トリガー

正しい設計:トリガーフレームワーク

基本構成:

trigger AccountTrigger on Account (
    before insert, before update,
    after insert, after update
) {
    AccountTriggerHandler handler = new AccountTriggerHandler();

    if (Trigger.isBefore) {
        if (Trigger.isInsert) handler.beforeInsert(Trigger.new);
        if (Trigger.isUpdate) handler.beforeUpdate(Trigger.new, Trigger.oldMap);
    }

    if (Trigger.isAfter) {
        if (Trigger.isInsert) handler.afterInsert(Trigger.new);
    }
}
public class AccountTriggerHandler {

    public void beforeInsert(List<Account> newRecords) {
        // ロジック
    }

    public void beforeUpdate(List<Account> newRecords, Map<Id, Account> oldMap) {
        // 差分処理
    }

    public void afterInsert(List<Account> newRecords) {
        // 関連レコード作成など
    }
}

差分検知が超重要

更新時は「何が変わったか」を見ないと事故る。

if (acc.Status__c != oldMap.get(acc.Id).Status__c) {
    // 状態変化時のみ処理
}

これをやらないと:

👉 無限更新ループ
👉 無駄なDML
👉 パフォーマンス劣化

再帰防止(これ知らないと死ぬ)

トリガー内で更新 → またトリガー発火 → 無限ループ

対策:

public class TriggerControl {
    public static Boolean isRunning = false;
}
if (TriggerControl.isRunning) return;
TriggerControl.isRunning = true;

Bulk対応(必須)

Salesforceは常に「複数レコード前提」

NG:

for (Account acc : Trigger.new) {
    insert new Contact(Name='test');
}

OK:

List<Contact> contacts = new List<Contact>();

for (Account acc : Trigger.new) {
    contacts.add(new Contact(Name='test'));
}

insert contacts;

まとめ

・トリガーは薄く
・ロジックはクラスへ
・差分検知は必須
・Bulk前提で書く
・再帰防止を忘れるな