# バックエンドポリシー

バックエンドに関する独立した判定を一つの正本で行う。

## 原則

| 原則 | 基準 |
|------|------|
| 適用条件を確認 | 元要件、変更契約、実在する影響経路に基づいて適用する |
| 事実を根拠にする | コード、契約、証跡で確認できる条件だけを判定する |
| 責務境界を守る | 判定対象の所有者と観測可能な影響を分けて確認する |
| 最小範囲に限定する | 今回の要求と因果関係のある範囲だけを判定する |
| 判定根拠を統一する | 元要件、変更契約、実在する影響経路から導けない例示を判断基準に追加しない |

## バックエンド 判定基準

### ヘキサゴナルアーキテクチャ（ポートとアダプター）

| 基準 | 判定 |
|------|------|
| ドメイン層にフレームワーク依存（@Entity, @Component等） | REJECT |
| Controller から Repository を直接参照 | REJECT。UseCase層を経由 |
| ドメイン層から外向きの依存（DB, HTTP等） | REJECT |
| adapter 間の直接依存（inbound → outbound） | REJECT |
| application / domain 層の型や識別子が、HTTP request/response、endpoint、status code 等のプロトコル固有の意味を持つ | REJECT。境界でユースケースの概念へ変換する。ドメイン上の語彙として Request 等を含むだけなら違反ではない |

### Request/Response DTO 設計

| 基準 | 判定 |
|------|------|
| ドメインモデルをそのままレスポンスに返す | REJECT |
| Request DTOにビジネスロジック | REJECT。バリデーションのみ許容 |
| Response DTOにドメインロジック（計算等） | REJECT |
| Request/Responseが同一の型 | REJECT |

### RESTful なアクション設計

| 基準 | 判定 |
|------|------|
| PUT/PATCH でドメイン操作（approve, cancel等） | REJECT。POST + 動詞サブリソース |
| 1つのエンドポイントで複数の操作を分岐 | REJECT。操作ごとにエンドポイントを分ける |
| DELETE で論理削除 | REJECT。POST + cancel 等の明示的操作 |

### バリデーション戦略

| 基準 | 判定 |
|------|------|
| ドメインの状態遷移ルールがAPI層にある | REJECT |
| ビジネスルール検証がControllerにある | REJECT。UseCase層に |
| 構造バリデーション（@NotBlank等）がドメインにある | REJECT。API層で |
| UseCase層のバリデーションがAggregate内にある | REJECT。Read Model参照はUseCase層 |

### 入口バリデーションの所有権

| 基準 | 判定 |
|------|------|
| 同じ入口・同じ条件を複数の検証機構で実装している | REJECT。実効的な所有者を一つにし、到達不能な側を削除する |
| 検証違反時のステータス・応答形状が明示した API 契約と一致していない（契約を定義せず既定の変換へ暗黙に依存している場合を含む） | REJECT。契約を明示し、変換を整備する |
| 検証違反時のステータスと応答形状を固定するテストがない | REJECT。実際に飛ぶ例外型を統合テストで確認 |
| 同じ信頼境界・同じ入力契約を持つ入口間で検証方針が不統一 | REJECT。差異の理由を明示するか方針を統一する |
| 外部エラー契約が実行環境のデフォルトロケールのメッセージに依存している | REJECT。安定したエラーコードまたは明示メッセージを契約にする |
| 制約値（最大長等）を検証と API 仕様が単一の定数で共有している | OK |

### 読み取りと書き込みの入口

| 基準 | 判定 |
|------|------|
| 問い合わせ層が保存・削除・外部呼び出し・コマンド送信を行う | REJECT |
| 読み取り用のクラス名やメソッド名なのに副作用を持つ | REJECT |
| 単純な参照APIが問い合わせ層を呼び、レスポンスDTOに変換するだけ | OK |
| 単純な状態変更APIが構造検証と認可境界の解決後にコマンドを1つ送るだけ | OK |
| Controller向けの読み取り調整役が認可境界、複数Read Model、ページング等を扱う | ApplicationService または ReadService として表現 |
| QueryHandler と同じ領域に QueryService という名前の送信側・調整側コンポーネントを置く | 警告。クエリ受信側と混同しやすい |
| 複数のRead Model参照、外部連携、複数コマンド、結果待機をControllerに置く | REJECT。UseCase層に分離 |
| UseCaseが別サービスへの薄い委譲だけでドメイン上の判断や調整を持たない | 削除を検討 |

### 例外階層設計

| 基準 | 判定 |
|------|------|
| ドメイン例外にHTTPステータスコードが含まれる | REJECT。ドメインはHTTPを知らない |
| 汎用的な Exception や RuntimeException を throw | REJECT。具体的な例外型を使う |
| try-catch の空 catch | REJECT |
| Controller 内で例外を握りつぶして 200 を返す | REJECT |
| 実際に到達し得る呼び出しパターン（別ロールの呼び出し者等）を 500 で表現する | REJECT。4xx で明示し、「到達しない」前提は認可で保証する |

### 例外変換のスコープ

| 基準 | 判定 |
|------|------|
| 各 endpoint が同じ try-catch や wrapper で例外を HTTP 表現に変換している | REJECT。HTTP adapter 境界の例外変換レイヤに分離 |
| 特定 API 固有の例外変換を global handler に追加する | スコープ過大。対象 API の境界へ閉じる |
| 認証失敗、入力検証、共通エラー形状など全 API 共通の変換 | OK。global な境界で扱う |
| 例外型から HTTP 表現への変換が application/domain 層にある | REJECT。HTTP adapter 境界で扱う |
| 同じ例外型を複数の変換レイヤが扱い、適用範囲・優先順位が契約化されていない | REJECT。単一の所有者へ寄せるか、非重複の適用条件を明示する |

### イミュータブル + require

| 基準 | 判定 |
|------|------|
| ドメインモデルに var フィールド | REJECT。`copy()` でイミュータブルに更新 |
| バリデーションなしのファクトリ | REJECT。`require` で不変条件を保証 |
| ドメインモデルが外部サービスを呼ぶ | REJECT。純粋な関数のみ |
| setter でフィールドを直接変更 | REJECT |

### 値オブジェクト

| 基準 | 判定 |
|------|------|
| 同じ型のIDが取り違えられる（orderId と customerId が両方 String） | 値オブジェクト化を検討 |
| 同じフィールドの組み合わせ（from/to等）が複数箇所に | 値オブジェクトに抽出 |
| 値オブジェクトに init ブロックがない | REJECT。不変条件を保証する |

### Read Model Entity（JPA Entity）

| 基準 | 判定 |
|------|------|
| ドメインモデルを JPA Entity として兼用 | REJECT。分離する |
| Entity に ビジネスロジック | REJECT。Entity はデータ構造のみ |
| Repository 実装がドメイン層にある | REJECT。adapter/outbound に |

### 構造化属性の永続化境界

| 基準 | 判定 |
|------|------|
| 全体を一括で読み書きし、検索・結合・参照整合性・部分更新が不要な有界の構造 | 構造化カラム（JSON等）を検討 |
| 参照整合性、独立したライフサイクル、他テーブルとの結合が重要 | 別テーブルへ正規化 |
| 必要な検索・索引・部分更新を DB の構造化カラム機能（jsonb 等）が保証でき、整合性要件も満たせる | 構造化カラムも選択可 |
| ドメイン型を汎用シリアライザで直接変換し、フィールド名を DB スキーマとして暗黙利用している | REJECT。永続化専用の表現または明示的な変換を挟む |

### 認証・認可の配置

| 基準 | 判定 |
|------|------|
| 認可ロジックが UseCase 層やドメイン層にある | REJECT。Controller層で |
| データアクセス制御が Controller にある | REJECT。UseCase層で |
| 認証処理が Controller 内にある | REJECT。Filter/Interceptor で |
| Application 層のサービスがセキュリティコンテキスト（現在ユーザーの解決等）を直接読む | REJECT。境界で解決し引数で渡す |
| 同じ認可チェックが Controller と下位層で重複している | REJECT。責務を一箇所へ一本化 |

### 呼び出し者とドメイン上のアクターの区別

| 基準 | 判定 |
|------|------|
| 呼び出し者を無条件に業務上の担当者として記録する | 警告。取り込み・代理・管理経路で破綻しないか確認 |
| 作成時の呼び出し者を、後続操作のアクターとして状態経由で流用する | REJECT。操作ごとに実行者を引数で渡す |
| 業務上の担当者がまだ確定しない段階で担当者フィールドを必須にする | 警告。担当者が確定する操作（承認・確定等）の時点で記録できないか確認 |
| 表示用に非正規化する氏名等を、その事実が確定する操作の境界で解決する | OK |
| 呼び出し者がリソースの構成員である前提の解決処理を、構成員以外も通る経路に置く | REJECT |

### UseCase のテスト

| 基準 | 判定 |
|------|------|
| ドメインモデルのテストにモックを使用 | REJECT。ドメインは純粋にテスト |
| UseCase テストで実DBに接続 | REJECT。モックを使う |
| テストがフレームワークの起動を必要とする | ユニットテストなら REJECT |
| 状態遷移の異常系テストがない | REJECT |

## アンチパターン検出

| パターン | 判定 |
|---------|------|
| Smart Controller | REJECT。Controller にビジネスロジックが集中する |
| Anemic Domain Model | REJECT。ドメインモデルが setter/getter だけのデータ構造になる |
| God Service | REJECT。1つの Service クラスに全操作が集中する |
| Repository直叩き | REJECT。Controller が Repository を直接参照する |
| ドメイン漏洩 | REJECT。adapter 層にドメインロジックが漏れる |
| Entity兼用 | REJECT。JPA Entity をドメインモデルとして使い回す |
| 例外握りつぶし | REJECT。空の catch ブロックで失敗を隠す |
| Magic String | REJECT。ステータス文字列などをハードコードする |
