──「クエリ最適化したのにイマイチ…」を卒業するために
「クエリはチューニングした。インデックスも貼った。それでもなんか UX が重いし、設計に不安がある。」
SaaS や業務 Web アプリを作っていると、わりと多くの人がこの壁にぶつかります。
原因はシンプルで、
クエリ最適化 = UX 向上の 1 ピースでしかない
からです。
この記事では、「クエリ最適化“以外”で、設計として必ずチェックしておきたいポイント」を
チェックリスト形式で整理します。
Next.js + Supabase の SaaS(マルチテナント前提)を念頭に書いていますが、スタック問わず使えます。
全体像:設計レビューで見るべき 9 つの観点
設計レビューで最低限押さえておきたいのは、この 9 項目です。
- データモデル・ドメイン設計
- API 設計(境界と契約)
- キャッシュ・読み取りパターン
- 書き込みパターン・整合性・排他
- エラー処理・フォールバック
- マルチテナント・セキュリティ設計
- 拡張性・変更容易性
- 観測性(ログ・メトリクス・トレース)
- レビュー用の「最後の一枚」チェックリスト
順番に見ていきます。
1. データモデル・ドメイン設計
1-1. ドメイン構造は一言で説明できるか?
- 「このアプリは、◯◯→△△→□□ という階層のデータ構造です」と、
非エンジニアにも説明できるか? - 同じ意味の概念が、別名のテーブルやカラムで二重管理されていないか?
例(よくある悪手):
-
projectsとaction_mapsが、ほぼ同じ役割で並存している -
goal_idとokr_idが混在している
構造がブレると、
- クエリは複雑
- UI は分かりにくい
- ロジックはバグりやすい
の三重苦になります。
1-2. ID・リレーションのルールが明確か?
特にマルチテナントでは以下を必ず確認します。
- すべての業務データに
tenant_id(必要ならworkspace_id)が付いているか? - テナントを跨ぐ参照が「物理的に」できない構造になっているか?
- ソフトデリート(論理削除)とハードデリート(物理削除)の方針がテーブルごとに明文化されているか?
2. API 設計(境界と契約)
2-1. 1 エンドポイント 1 責務になっているか?
やりがちなのが「なんでも屋 API」です。
- 悪い例:
-
POST /tasks/complex
→ 取得・集計・作成・更新を全部やる
-
- 良い例:
-
GET /tasks(一覧取得) -
POST /tasks(作成) -
GET /me/today(ユースケース単位のサマリ API)
-
リソース単位の API と ユースケース単位の API を、意図的に分けるだけでも設計はかなり整理されます。
2-2. レスポンスの粒度(過不足)
- オーバーフェッチ:
- 画面で使わない情報まで全部返してしまう
- アンダーフェッチ:
- 実際の画面表示に必要な情報を得るために、3〜4 回 API を叩かないといけない
「この画面のための API」と「汎用 API」を分ける、
あるいは include=xxx のようなオプションで制御する設計を検討しましょう。
2-3. 変更に強いか?
- レスポンス形式を変える時に、既存クライアントが壊れない工夫をしているか?
- 外部公開 API にした瞬間に「詰む」ような命名・構造になっていないか?
将来の自分(もしくは別チーム)が、仕様変更で泣かないような契約設計が重要です。
3. キャッシュ・読み取りパターン
クエリ最適化と並んで効くのが「どこで・どう読むか」です。
3-1. どこでキャッシュするかを決めているか?
- フロント側(React Query など)でキャッシュする前提になっているか?
- サーバー側でキャッシュ or マテリアライズしたほうが良い重い集計はどれか?
「全部 DB に毎回取りに行く」前提だと、どこかで限界が来ます。
3-2. フェッチ戦略は統一されているか?
Next.js なら、例えばルールを決めておくと楽です。
- 一覧・サマリ:Server Components / Route Handler で SSR
- ユーザー操作直結の細かい更新:Client Components + RPC API
「どこでデータを取るか」がコンポーネントごとにバラバラだと、
キャッシュ・再検証・エラー処理が破綻しやすくなります。
3-3. ページング・無限スクロールの設計
- ログや履歴、タスクなど「増え続けるデータ」は必ずページング前提にする
- 「全件取得」は原則禁止
UX 的にも、1000 行を一気に描画する UI は誰も喜びません。
4. 書き込みパターン・整合性・排他
ここを雑にすると、「データはあるのに信用できない」状態になります。
4-1. トランザクション境界は意識されているか?
例:
- タスク完了 → 実績時間の記録 → OKR の進捗再計算
これをバラバラのクエリで実行していると、途中失敗で矛盾状態になります。
「まとめて 1 トランザクションにすべき処理」を洗い出しておきましょう。
4-2. 同時更新の扱い(レースコンディション)
- 同じタスクを 2 人が同時編集したらどうなるか?
- 2 つのタブで同じ画面を開いて保存したら?
設計の選択肢としては:
- 最後の書き込み勝ち(last write wins)+ユーザー通知
- バージョン番号/
updated_atによる楽観ロック - 強制ロック(誰かが編集している間は他ユーザーは編集不可)
いずれにせよ、「何も決めていない」が最悪です。
4-3. 冪等性(いべとうせい)
- 同じリクエストが 2 回送られても結果が壊れないか?
- タスク完了 API を 2 回叩いても、完了フラグが二重に増えたりしないか?
- リトライ前提の設計(キュー・ワーカーなど)では必須
5. エラー処理・フォールバック
ユーザーの「不信感」は、エラー設計の甘さから生まれます。
5-1. ユーザーは次に何をすればいいか分かるか?
- 「保存に失敗しました(コード: 500)」で終わっていないか?
- 例:
- ネットワークエラー → 「接続が不安定です。〇〇ボタンで再試行できます」
- 認証切れ → 「再ログインが必要です」+ログイン画面へ誘導
- 権限不足 → 「この操作には管理者権限が必要です」
5-2. 入力内容は消えないか?
- 長文フォームで送信エラーが出た時、入力内容は保持されるか?
- 下書きとしてローカル保存 or サーバー側にオートセーブしているか?
「1 回のエラーで 30 分の入力が消える」アプリは、それだけで信用を失います。
5-3. 外部 API 障害時の振る舞い
- Google カレンダーなど外部サービスが落ちたとき、
- アプリ全体が巻き添えで壊れないか?
- 「今はカレンダー連携だけ一時停止」などの degrade 設計があるか?
6. マルチテナント・セキュリティ設計
RLS の有無に関係なく、「テナント境界」は設計段階で握っておく必要があります。
6-1. テナント境界チェック
- すべての API 入口で
- セッションの
tenant_id - リクエスト対象の
tenant_id
を突き合わせる仕組みがあるか?
- セッションの
- 「tenant_id を指定しなくても動く API」が紛れ込んでいないか?
6-2. 権限モデル
-
owner / admin / member / guestなどのロール設計はあるか? - 「誰が」「どのワークスペースの」「どのリソース」に対して、
- 読める
- 書ける
- 削除できる
- メンバー管理できる
がマトリクスで説明できるか?
6-3. 機密情報の扱い
- 個人情報・機微情報は、必要以上にログや監視ツールに流れていないか?
- 暗号化・マスキングすべきカラムは設計段階で洗い出されているか?
7. 拡張性・変更容易性
「半年後の自分が追加したいであろう機能」を想像してみます。
7-1. よくありそうな変更に耐えられるか?
例:
- タスクにサブタスクを入れたい
- OKR にタグやオーナーを追加したい
- 習慣に「グループ」や「カテゴリ」を持たせたい
このとき、
- テーブル構造が完全に作り直しにならないか?
- API のレスポンスを「足すだけ」で済むか?(既存クライアントはそのまま)
を軽くシミュレーションしておくと、破綻しにくくなります。
7-2. モジュール分割・ファイル構成
- ディレクトリがドメインごとに分かれているか?
-
/okrs,/tasks,/habits,/workspaceなど
-
- 「なんでも入っている巨大 util ファイル」が存在していないか?
- 1 ファイルあたりの行数上限(例:500 行)ルールは守られているか?
8. 観測性(ログ・メトリクス・トレース)
設計がどれだけ良くても、「何が起きているか見えない」と改善できません。
8-1. ログ設計
- API レベルで最低限、「誰が」「どのテナント/ワークスペースで」「どのリソースに対して何をしたか」は残せるか?
- エラーログとユーザー操作ログがごちゃ混ぜになっていないか?
8-2. メトリクス
- P95 レイテンシ・エラー率・Disk IO など、インフラ系メトリクスは取れているか?
- プロダクト的な指標(例:タスク完了率、日次アクティブユーザー数)を計測する仕組みはあるか?
「UX が悪い気がする」を「◯◯画面の P95 が 1.8 秒超えている」に翻訳できると、
改善の議論が一気に具体化します。
9. 設計レビュー用「最後の一枚」チェックリスト
実際のレビューやセルフチェックで使えるように、
ここまでの内容を 1 枚に凝縮したチェックリストを置いておきます。
設計レビュー前に、最低限これだけは YES にしたい項目:
- この機能のデータ構造を“図なしで”一言で説明できる
- すべての業務データに
tenant_id(+必要ならworkspace_id)が付いている - API は「1 エンドポイント 1 責務」になっている(なんでも屋がない)
- その画面専用の読み取り API と、汎用リソース API の役割が整理されている
- 読み取りのキャッシュ戦略(どこでキャッシュするか)が決まっている
- 複数テーブルを跨ぐ更新処理に、トランザクション設計がある
- 同時更新(レースコンディション)時の振る舞いが説明できる
- 主要な書き込みパスは「冪等になる」ように設計されている
- エラー時にユーザーが「次に何をすればいいか」分かる UI になっている
- テナント境界・権限のルールを A4 1 枚で説明できる
- 「半年後にありそうな機能追加」に対して、スキーマと API が致命的に硬くない
- 運用開始後に見るメトリクス・ログ(どこを見れば異常に気づけるか)が決まっている
まとめ
クエリ最適化はもちろん大事ですが、それだけでは UX は良くなりません。
- データモデル
- API の責務
- キャッシュ・書き込みパターン
- エラーとセキュリティ
- 拡張性と観測性
といった「設計の地盤」を固めることで、
「なんか遅い・怖い・分かりづらい」を
「安心して触れる・成長に耐えられる SaaS」
に変えていくことができます。
チームで設計レビューをするときや、
自分のプロダクトを一段引いて見直したいときに、
この記事のチェックリストをそのまま使ってもらえればと思います。