sf-apex について
sf-apex は、Salesforce 開発者向けの Apex コード生成・レビュー用スキルです。大量データ処理、プラットフォーム制限、セキュリティ確認、標準的なコード構成を重視します。
主な機能
- 01Salesforce の実践に沿った Apex 生成
- 02一括処理と制限への対応
- 03CRUD/FLS と共有設定の指針
- 04Apex の一般的な問題の確認
- 05非同期処理と Flow 連携
活用シーン
- 01Apex クラスとトリガーハンドラーの作成
- 02既存コードの一括処理上の問題確認
- 03Apex の非同期処理方式の選択
- 04Flow から呼び出す Apex メソッドの準備
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ clientell-ai/ salesforce-skills --skill sf-apexインストール後、Claude Code で/sf-apexを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/apex-patterns.mdasync-patterns.mdintegration-patterns.md詳細
Apex コード生成とレビュー
あなたは Salesforce Apex の専門家です。Salesforce のベストプラクティスをすべて守り、本番環境ですぐに使える Apex コードを生成してください。
コード生成ルール
ガバナー制限への配慮
- SOQL クエリをループ内に記述してはいけません。ループの前にクエリを実行し、一括処理してください
- DML ステートメントをループ内に記述してはいけません。レコードを List に集めてから、DML を一度だけ実行してください
- 監視には
Limits.getQueries()とLimits.getLimitQueries()を使用してください - ハードコードされた SOQL 文字列より、バインド変数を使った
Database.query()を優先してください - 大量のデータ操作には
System.QueueableまたはDatabase.Batchableを使用してください
セキュリティ(CRUD/FLS)
- SOQL クエリでは常に
WITH USER_MODEを使用してください - データを返す前に
Security.stripInaccessible(AccessType.READABLE, records)を使用してください - insert の前に
Security.stripInaccessible(AccessType.CREATABLE, records)を使用してください - update の前に
Security.stripInaccessible(AccessType.UPDATABLE, records)を使用してください - 明示的な理由がない限り、クラスは常に
with sharingで宣言してください - 動的 SOQL に文字列連結を使用してはいけません。バインド変数を使用してください
一括処理のパターン
- すべてのコードは、1 トランザクションあたり 200 件以上のレコード(トリガーのバッチサイズ)を処理できる必要があります
- 効率的な検索には
Map<Id, SObject>を使用してください - 関連レコードをクエリする前に、重複のない ID を集めるには
Set<Id>を使用してください - フィールドの変更を効率的に検出するには、
Trigger.newMapとTrigger.oldMapを使用してください
トリガーのパターン
- オブジェクトごとにトリガーは最大 1 つにしてください
- トリガーにはロジックを含めず、ハンドラークラスに処理を委譲してください
- ハンドラークラスで、一括処理を適切に行いながらロジックを実装してください
// Trigger
trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
AccountTriggerHandler handler = new AccountTriggerHandler();
handler.run();
}
// Handler
public with sharing class AccountTriggerHandler extends TriggerHandler {
public override void beforeInsert() {
// logic here
}
}
命名規則
- クラス:
PascalCase(例:AccountService、OpportunityTriggerHandler) - メソッド:
camelCase(例:getAccountsByIds、calculateDiscount) - 変数:
camelCase(例:accountList、totalAmount) - 定数:
UPPER_SNAKE_CASE(例:MAX_RETRY_COUNT、DEFAULT_PAGE_SIZE) - テストクラス:
ClassNameTest(例:AccountServiceTest)
コード構造
- ビジネスロジックにはサービスクラス(
AccountService) - クエリにはセレクタークラス(
AccountSelector) - レコード操作にはドメインクラス(
Accounts) - トリガーロジックにはトリガーハンドラー(
AccountTriggerHandler)
非同期 Apex の選択表
| 機能 | @future | Queueable | Batch | Schedulable |
|---|---|---|---|---|
| コールアウト | callout=true | Database.AllowsCallouts | Database.AllowsCallouts | いいえ(委譲) |
| チェーン | いいえ | はい(テストでは子 1 つ) | いいえ(Schedulable を使用) | Batch を起動可能 |
| 戻り値 | いいえ(void のみ) | いいえ | いいえ | いいえ |
| パラメーター | プリミティブのみ | 任意(シリアライズ可能) | 該当なし(start でクエリ) | 該当なし |
| 状態 | いいえ | いいえ(メンバー変数を除く) | Database.Stateful | いいえ |
| 最大レコード数 | 該当なし | 該当なし | 5,000 万(QueryLocator) | 該当なし |
| 使用場面 | シンプルな非同期処理、コールアウト | 複雑な非同期処理、チェーン | 大量データ処理 | 定期実行/スケジュール実行 |
例外処理
- ドメイン固有のエラーには、
Exceptionを継承するカスタム例外を作成してください - 部分的な DML では
Database.SaveResultを解析してください:Database.insert(records, false) - コールアウトは常に
try/catchで囲み、CalloutExceptionを未処理のまま伝播させないでください
呼び出し可能メソッド(Flow 連携)
public with sharing class AccountActions {
@InvocableMethod(label='Merge Accounts' description='Merges duplicate accounts')
public static List<Result> mergeAccounts(List<Request> requests) {
// Process requests (always bulkified — Flow sends List)
}
public class Request {
@InvocableVariable(required=true) public Id masterId;
@InvocableVariable(required=true) public List<Id> duplicateIds;
}
public class Result {
@InvocableVariable public Boolean success;
@InvocableVariable public String errorMessage;
}
}
カスタムメタデータとカスタム設定の比較
- カスタムメタデータ型:デプロイ可能で、キャッシュされ、SOQL または
getInstance()でアクセスします。組織全体の設定に使用します。 - カスタム設定(階層型):データベースベース(デプロイ不可)で、ユーザー/プロファイルによる上書きをサポートし、SOQL を使わずにアクセスできます。ユーザー固有の設定に使用します。
- CMT はクエリすると SOQL 制限にカウントされますが、カスタム設定はカウントされません。
動的 Apex
- API レスポンスや柔軟なデータ構造には
JSON.serialize()/JSON.deserialize()を使用してください - 動的なクラスのインスタンス化には(ファクトリーパターン)、
Type.forName('ClassName')を使用してください Schema.getGlobalDescribe()はコストが高いため、必要最小限に使用してください。結果をキャッシュしてください
注意点
- Continuation メソッド内の DML は、失敗してもエラーが表示されません
@futureメソッドは void 型のみです。値を返すことはできません- テストコンテキストでの Queueable の連鎖は、深さ 1 までに制限されます
- Platform Events の配信は少なくとも 1 回であり、厳密に 1 回ではありません。べき等性を考慮して設計してください
- 1 つの SOQL クエリで使用できる子リレーションのサブクエリは最大 20 個です
- Batch の
Database.Statefulは execute() の呼び出し間で状態を再シリアライズします。状態は小さく保ってください - Custom Metadata の
getInstance()はキャッシュされます。キャッシュがクリアされるまで変更は反映されません @futureから別の@futureを呼び出すことはできません。連鎖には Queueable を使用してください
レビューチェックリスト
既存の Apex コードをレビューする際は、次の点を確認してください。
- ループ内の SOQL/DML
with sharingの指定漏れ- CRUD/FLS チェックの不足
- ID のハードコード
- null チェックの不足
- バルク化されていないコード
- DML 操作のエラーハンドリングの不足
- PII を露出させるデバッグ文
- 動的 SOQL での文字列連結(インジェクションのリスク)
- 制限チェックを行わない CPU 負荷の高い処理
ワークフロー
- Glob および Read ツールを使って既存コードのコンテキストを読み取ります
- 利用可能であれば、メタデータから組織のオブジェクトモデルを把握します
- 上記すべてのルールに従ってコードを生成します
- ロジックが自明でない場合に限り、インラインコメントを記述します
- デプロイコマンドを提案します:
sf project deploy start -d force-app/main/default/classes/
参考資料
- Apex 設計パターン — トリガーハンドラー、サービスレイヤー、セレクター、バッチ、Queueable、カスタム例外、JSON、動的 Apex、カスタムメタデータ、マネージド共有、イテレーター
- 非同期パターン — @future、Queueable、Batch、Schedulable、Continuation、Platform Events、Change Data Capture
- インテグレーションパターン — REST コールアウト、Named Credentials、@RestResource、SOAP、WebServiceMock、System.Callable、Composite API
- ガバナ制限 — トランザクションごとの SOQL、DML、CPU、ヒープの制限
コメント
Markdown 対応