はじめに:なぜ参照系の設計は迷走するのか
システムの複雑さは、更新(Command)よりも参照(Query)に現れます。
「1件取得」から始まったクエリが、いつの間にか「集計・判定・評価」が複雑に絡み合った秘伝のタレのような巨大SQLに化けてしまうのは、現場の「あるある」です。
パフォーマンスを優先してSQLにロジックを詰め込めばユニットテストが困難になり、保守性を優先してすべてをAppで処理すればパフォーマンスが崩壊する。このトレードオフを乗りこなすための「クエリ・スタイルガイド」を提案します。
1. 参照系を構成する「4つの基本パターン」
まず、クエリの責務を以下の4つに分解して定義しましょう。これらを組み合わせることで、複雑な参照系を整理できます。
| # | パターン名 | 問いの性質 | 戻り値 |
|---|---|---|---|
| 1 | Finder(取得) | 「何があるか?」 | Entity / List |
| 2 | Predicate(判定) | 「~であるか?」 | Boolean |
| 3 | Aggregator(集計) | 「いくらか? 何件か?」 | Number / Summary |
| 4 | Evaluator(評価) | 「条件を満たすか?(集計+判定)」 | Result / Enum |
各パターンの詳細説明
- Finder(取得): 特定の識別子や条件に基づいて、データを「そのまま」取り出す最も基本的な形です。
- Predicate(判定): 対象が特定の状態にあるか、あるいは条件に合致するかのみを判断します。ロジックの「分岐」の根拠となります。
- Aggregator(集計): 複数のレコードを足し合わせたり、平均を出したりして、データの「傾向」や「総数」を数値化します。
- Evaluator(評価): 「集計した数字」と「ビジネスルール」を照らし合わせ、最終的なステータスを決定する最も高度なパターンです。
2. 核心:DBとAppの「境界線」をどう引くか
最大の争点は、「どこまでSQLでやり、どこからApp(コード)で書くか」です。これを決めるのは、以下の2つの評価軸です。
① DB(SQL)が担うべきこと
- 対象: 大量のレコードを走査し、結果を絞り込む・数える必要がある場合。
- 判断基準: Appに全データを送るとパフォーマンスが壊滅するなら、DBの出番。
- 例: 10万件のデータから特定の条件で絞り込む(Filter)、全件の合計や平均を出す(Sum/Avg)。
② App(コード)が担うべきこと
- 対象: 判定条件が複雑なビジネスルールに基づき、変更頻度が高い場合。
- 判断基準: 「そのロジックのユニットテストを書きたいか?」。もしYESなら、App側にロジックを置くべき。
- 例: 会員ランクやキャンペーン期間による割引の適用判定(Predicate/Evaluator)。
黄金律:DBは「事実」を作り、Appは「意味」を決める
最も推奨される分業モデルは、「DBから生の数字(事実)を受け取り、App側でビジネス上の意味を判断する」という形です。
- DBの仕事: 「現在の回答数は102件である」という事実(Aggregator)を高速に返す。
- Appの仕事: 「100件を超えているから、このアンケートは締め切り(Evaluator)である」という解釈を下す。
3. 実践:CTEを用いた「SQLのレイヤー化」
「混在して難しい」という問題への技術的回答は、SQL内でのレイヤー分離です。CTE(共通テーブル式)を使い、[集計] → [評価] のステップを明文化します。
WITH stats AS (
-- 【Step 1: Aggregator】DBの得意分野:大量集計
SELECT survey_id, COUNT(*) as cnt FROM responses GROUP BY survey_id
),
evaluation AS (
-- 【Step 2: Evaluator】Appに送る前の「前判定」
-- ※ ここでの判定は、WHERE句での絞り込みに使うものに限定するのが理想
SELECT
s.id,
CASE WHEN stats.cnt >= s.limit_count THEN true ELSE false END as is_full
FROM surveys s
LEFT JOIN stats ON s.id = stats.survey_id
)
SELECT s.title, e.is_full FROM surveys s JOIN evaluation e ON s.id = e.id;
4. この設計を支える「3つのバックボーン」
このスタイルガイドは、以下の著名な設計思想を現代的に解釈し直したものです。
① Specification Pattern (DDD / Eric Evans)
「あるオブジェクトが条件を満たしているか」という判定(Predicate)のロジックを独立させるパターンです。
「ビジネスルールなら、DBではなくSpecification(仕様)としてAppに置くべきだ」という強い根拠になります。
② CQRS & Projections (Greg Young)
更新系と参照系を分離し、参照系を「画面が必要な形」に合わせて再構成(投影)する考え方です。
「集計と判定が混ざった複雑なクエリ」は、単なるDB取得ではなく、表示専用のモデル(Read Model)を作るプロセスだと捉えることができます。
③ "Database is a Detail" (Clean Architecture / Uncle Bob)
「DBは詳細(保存先)に過ぎず、ビジネスロジックを知るべきではない」という原則です。
SQLにビジネスロジック(複雑なEvaluator)を詰め込みすぎないための、強力なアーキテクチャ上の警告となります。
まとめ:スタイルガイドへの記載案
チームで共有するためのエッセンスです。
- 判定(Predicate)が単純ならSQLの WHERE で、複雑ならAppの Logic で。
- 集計(Aggregator)はDBに任せる。ただし、結果の「解釈」はAppで行う。
- 命名規則を徹底し、戻り値が「単なるデータ」なのか「計算済みの評価」なのかを明確にする。
参照系は、ユーザーが意思決定を行うための「情報の窓口」です。単にデータを流すのではなく、そのクエリが「何を判定しようとしているのか」を整理することで、システムの柔軟性は劇的に向上します。