# Query Delegation Pattern

クエリの「構築」と「実行」を分離する、ApexEloquent の設計哲学を解説します。Salesforce 開発で広く採用される Selector Pattern の長期運用課題から出発し、Query Delegation Pattern がなぜそれを解くのか、ApexEloquent がそれをどう体現しているのかをまとめます。

## Introduction

Salesforce 開発の現場では、データアクセスを整理する手段として **Selector Pattern** が広く採用されてきました。クエリをひとつの場所に集めることで、ビジネスロジック側からは生 SOQL を見せず、メソッド呼び出しだけでデータが取れる、というシンプルな利点があります。

しかし、Selector Pattern を中規模以上のプロジェクトで長期運用すると、「ちょっと違うクエリが欲しい」という要求がたびたび発生し、メソッドや引数を追加するパッチが積み重なっていきます。Selector のクラスは膨らみ、当初のシンプルさは少しずつ失われていきます。さらに、テスト自体が DB を必要とするため、結合テストしか書けないという制約もついて回ります。

### Selector Pattern の長期運用で起きること

- ユースケースごとに微妙に違うクエリが必要になり、メソッドや引数が継ぎ足されて Selector が肥大化する
- 用途別に Selector を分割すると、似たような Selector クラスとインターフェースが大量に生まれる
- フラグ引数や条件分岐が積み上がり、メソッドの読みやすさと保守性が下がっていく
- あるユースケース向けのクエリ変更が、別の機能に予期しない影響を与える
- テストは Selector に対するモッククラスの実装と DB アクセスを伴う結合テストに偏る

### 「分離」が必要だと感じた理由

これらの状況に向き合っていくうちに、ひとつの問いに辿り着きました。

> 「クエリの構築とクエリの実行は、本当に同じ責務なのか?」

どのオブジェクトに、どんな条件でクエリを投げるかを決めるのは、ドメインのルールやユースケースに深く根ざした **ビジネスロジックの一部** です。一方、組み立てられたクエリを実際に DB に投げ、結果を返す処理は、純粋な **I/O** でしかありません。

この 2 つを Selector の中に同居させるのではなく、明確に切り分けたい。そう考えた末に行き着いたのが Query Delegation Pattern という発想です。

## Problems with Traditional Patterns

### クエリ構築と I/O が混ざることで起きる問題

Selector は典型的に、「何のデータをどう取り出すか」を決めるクエリ構築と、「実際に DB に問い合わせて結果を取り出す」実行処理の両方を抱えます。クエリ構築はビジネスロジックに近く、実行はインフラ層に属する、と整理すると、本来この 2 つは **別の責務** です。

これらが Selector の中に混ざると、徐々に次のような状態に陥っていきます。

- クエリの組み立てが Selector の内側に閉じ込められ、ドメイン側からは「何をどう取りに行っているのか」が見えづらくなる
- クエリの修正が、その Selector を使うすべての機能に波及するリスクを伴う
- テストには Selector インターフェースに対するモック実装が必要で、テストごとに使い分けるためにバリエーションを準備する手間が増える
- ひとつの Selector に処理が集中すると、メソッドを足し続ける運用になり、継続的な機能開発の足を引っ張る
- ユースケースごとに Selector を分割しても、似たようなインターフェースと実装クラスが何個も並ぶことになり、テスト構築への心理的負担が増えていく

理想的には、「どんなデータが必要か」はユースケース側 (ドメイン側) で明示的に表現し、Selector には「実行」だけを委ねる形にしたい。これが Query Delegation Pattern が出発する地点です。

### 再利用性と文脈の喪失

Selector の魅力のひとつは **再利用性** です。「特定条件で絞り込んでレコードを取得する」メソッドがひとつあれば、複数のユースケースから共通で使えます。

しかしこの再利用性は、しばしば **ビジネス上の意図の不明瞭さ** と引き換えになります。汎用的に設計された Selector メソッドは、「なぜそのデータが必要なのか」「どんな文脈で使われるのか」という情報を失いがちです。

> 💭 Selector のメソッドは、汎用的に作るほど「具体的なビジネス意図」から遠ざかっていく。

この問題はテストの観点からも軽視できません。ビジネス意図が曖昧なメソッドは、テストケースを書くときに「何を検証すればよいか」が漠然としやすく、結果としてテストの厚みが不足しがちになります。

## What is Query Delegation Pattern?

### 基本コンセプト

Query Delegation Pattern は、「クエリ構築」と「クエリ実行」の責務を明確に分離するアプローチです。

| 責務 | 担い手 | 性質 |
|---|---|---|
| **Query Construction** (クエリ構築) | ドメイン側 (Usecase) | ビジネスルールに基づいて条件を組み立てる |
| **Query Execution** (クエリ実行) | ApexEloquent 内蔵の `IEloquent` | 組み立て済みクエリを受け取り、DB との I/O を引き受ける |

従来の Selector は両方の責務を抱えていましたが、Query Delegation Pattern では次のように分けます。

1. **ドメイン側** (Usecase) が、クエリの設計図を組み立てる
2. **実行側** (`IEloquent`) は、組み立て済みクエリを受け取り、それを実行する

> 💡 実行側が関心を持つのは「何を取るか」ではなく「どう取るか」だけ、という設計に切り替わります。

### ApexEloquent における役割分担

ApexEloquent はこの分離をフレームワークレベルで実現するために、3 つのコア機能を提供します。

| 役割 | クラス | 説明 |
|---|---|---|
| **Query Builder** | `Scribe` | クエリの設計図を、型ヒントとメソッドチェーンで組み立てる immutable なビルダー |
| **Data Access** | `IEloquent` (`Eloquent` / `MockEloquent`) | 設計図を受け取って SOQL を発行する側。本番は `Eloquent`、テストは `MockEloquent` を DI で差し替える |
| **Record Wrapper** | `IEntry` (`Entry` / `MockEntry`) | 取得結果のラッパー。SObject と AggregateResult を同じインターフェースで扱える |

## Query Reusability

### クエリの再利用をどう扱うか

Query Delegation Pattern では、クエリは基本的にユースケースごとに個別に組み立てる方針を取ります。そのほうがビジネス意図がコードに残るからです。一方で、「このクエリは複数のユースケースから共通で使いたい」という場面も現実には存在します。

ApexEloquent はこの問題に対して、**2 つの選択肢** を提供します。チームの規模、ドメインの複雑さ、クエリの共通度合いを見て、「このプロジェクトではどちらに倒すか」を意識的に選びます。

### 選択肢 1: Usecase 内に閉じ込める

ビジネスロジックのそばで `Scribe` を組み立て、文脈を完全に保つアプローチ。Apex Stem の [Handler-Usecase Architecture](/ja/apex-stem/docs/handler-usecase-architecture) ではこちらが原則です。

メリット:

- なぜそのデータが必要なのか、という文脈がコードに残る
- 別のユースケースへの予期しない影響が起きにくい
- ユースケースを読むだけで、必要なデータの全体像が見える

### 選択肢 2: Selector 派生としての「クエリ保管庫」を持つ

共通化したい `Scribe` をユーティリティ的なクラスに切り出し、必要なユースケースだけが明示的に取り込むアプローチ。Selector Pattern の派生として、「クエリの部品」を提供するスタイルです。

メリット:

- 重複が大きいクエリを 1 箇所に集約できる
- 共通化したクエリが Scribe オブジェクトとして渡されるので、ユースケース側でさらにチェーンメソッドで条件を継ぎ足せる
- Selector Pattern からの段階的移行が自然に取れる

利用側のイメージ:

```apex
// 1. Id で絞り込む基本クエリを保管庫から取得
Scribe oppScribe = OpportunityVault.getById(oppId);

// 2. ユースケース固有の条件を継ぎ足す
oppScribe = OpportunityVault.addNameCondition(oppScribe, '%TestName%');

// 3. 見積を子サブクエリとして追加
List<String> quoteFields = new List<String>{ 'Id', 'Name', 'GrandTotal' };
oppScribe = OpportunityVault.addQuotes(oppScribe, quoteFields);

// 4. 実行は IEloquent に委譲 (Query Delegation)
List<IEntry> entries = this.eloquent.get(oppScribe);
```

ここでは商談のクエリ保管庫として **Vault** という単語を使用しています。

保管庫の各メソッドは `Scribe` を返すだけで、SOQL の発行は最後の `IEloquent.get(scribe)` に集約されます。これが Query Delegation Pattern と「保管庫」スタイルの両立を成立させる仕組みです。従来の Selector のように 「メソッドの中で SOQL を実行して結果を返す」形だと、ユースケース側から条件を継ぎ足す余地がなく、結局メソッドや引数のバリエーションを増やすしかなくなります。

### どちらを選んでも成立する理由

いずれの場合も、`Scribe` が `.field()` / `.whereEqual()` などのメソッドチェーンで **クエリを部品として組み立てられる** 性質を持っていることが、両方の選択肢を成立させています。ユースケース内で 1 から組むことも、保管庫から取ってきた途中状態の `Scribe` に対してさらに `.whereEqual(...)` を継ぎ足すことも、同じ書き味で扱えます。

> 🎯 どちらが正解、という話ではありません。チームの状況に合わせて選び、必要なら片方からもう片方へ移行することもできます。

## Comparison with Traditional Patterns

### 何がどう変わるのか

| 観点 | Traditional Selector | Query Delegation Pattern |
|---|---|---|
| **責務の置き方** | クエリ組み立てと DB 実行が同居 | ドメイン側がクエリを組み立て、`IEloquent` は実行のみ |
| **長期運用** | パッチの積み重ねで肥大化、シンプルさを失う | 構造が崩れにくく、追加機能を継ぎ足しやすい |
| **テストの書き方** | DB を必要とする結合テスト中心 | `MockEloquent` 差し替えで DB なしの単体テストが書ける |
| **意図の可視化** | 汎用メソッド化で文脈が失われがち | ユースケースのそばに組み立てがあり、なぜそのデータが必要かが残る |

## Implementation in ApexEloquent

ApexEloquent は Query Delegation Pattern をフレームワークレベルで体現します。実装上の主な特徴は以下のとおりです。

### 動的なクエリ構築

ドメイン側が `Scribe` でクエリを組み立て、文脈に応じて条件を継ぎ足します。`Scribe` は immutable なので、共通の `Scribe` を起点に「先月用」「今期用」など複数のクエリを派生させても、互いに影響しません。

### クエリ実行は ApexEloquent 内蔵で済む

実行側は ApexEloquent 内蔵の `IEloquent` が共通で引き受けます。自前で Selector を量産する必要がなく、組み立てた `Scribe` を渡すだけで SOQL が発行されます。

### モック差し替えで DB なし単体テスト

構築と実行が分かれているので、`IEloquent` を `MockEloquent` に差し替えるだけで DB なしの単体テストが書けます。`MockEloquent` には `upsertedRecordsAt(label)` / `deletedCountAt(label)` といった Spy もあり、何が DML されたかをアサートできます。

### 書き込み不可項目もモック可能

数式項目・ロールアップ・親リレーション・auto-number など、通常は書き込み不可な項目も `MockEntry` 側で自由に値を入れられます。これにより、本番では計算結果でしか取れない値も、テストでは「この値が返る前提でロジックを検証する」という形で扱えます。

### 実装の入口

実装の入り口としては、まず [Scribe でクエリを組み立てる](/ja/apex-stem/docs/apex-eloquent-scribe-guide) を、続いて [データ取得と DML、IEntry、Mock](/ja/apex-stem/docs/apex-eloquent-data-access) を読むと、Query Delegation の実コードへの落とし込みがそのまま見えます。

## Summary

### Query Delegation Pattern が解くもの

Query Delegation Pattern は、Selector が抱えがちな「クエリ構築」と「DB 実行」の責務の混在を、根本から分けてしまうアプローチです。

主な効果:

- 責務の明確な分離による、長期運用での保守性向上
- 複雑なモック実装を書かずに済むテスト構築の手軽さ
- クエリ意図の可視化によるドメインモデルの健全化
- ビジネスロジックとデータアクセス層の結合度低下

> Query Delegation Pattern は単なる技術的な工夫ではなく、データアクセス層の設計に対する考え方そのものを、よりクリーンな方向に押し出すための哲学です。

### 関連ドキュメント

- [ApexEloquent トップ](/ja/apexeloquent): Query Delegation Pattern を採用した OSS の全体像
- [Apex Stem](/ja/apex-stem): ApexEloquent を含む 4 OSS と Handler-Usecase Architecture の組み合わせ
- [Apex における Repository パターンの試行錯誤と内蔵化](/ja/apex-stem/docs/repository-pattern-challenges-builtin-solution-apex): なぜ Repository を ApexEloquent に内蔵したか (Selector Pattern との関係)
- [Apex の生 SOQL から、チェーンメソッドで組み立てる ORM へ](/ja/apex-stem/docs/dynamic-query-creation-apex-eloquent): 生 SOQL から Scribe への移行入門
- [MockEntry: Apex のテストデータ作成を成立させる仕組み](/ja/apex-stem/docs/apex-eloquent-mockentry-deep-dive): モック側の Deep Dive
- [モックテストの偽陽性を検知する: SELECT 漏れの安全網](/ja/apex-stem/docs/false-positive-detection-comprehensive-guide): Scribe との連携で SELECT 漏れを単体テストで検知
- [Handler-Usecase Architecture](/ja/apex-stem/docs/handler-usecase-architecture): Query Delegation を実コードへ落とし込む場所 (Usecase 層)
- [ApexEloquent ガイド](/ja/apex-stem/docs/apex-eloquent-guide): Scribe / IEloquent / IEntry の使い方と API リファレンス
