Layered Architecture — *責務をレイヤーで分ける*
Layered Architecture — *責務をレイヤーで分ける*
🎯 このレッスンを読んだら
このレッスンをすべて読み終えると、以下の3つを自信を持って実践できるようになります。
- ▸✅ Controller → Service → Repository の3層における責務の分離
- ▸✅
@Transactionalを適用すべき正確な位置 - ▸✅ DTO ↔ Entity 変換の責務を誰が持つべきか
学習目標をチェックリストとしてすべて答えられるようになったら、レッスンを閉じてください。
なぜ *3層* に分けるのか
核心を一言で
Spring バックエンドは通常、Controller → Service → Repository の3層にコードを分けます。各層に明確な責務を持たせることで、保守性の高いコードを実現する標準的なアーキテクチャです。
各層の役割
Controller: HTTP の出入り口。リクエストの受信・検証・レスポンス生成。ビジネスロジックは持たない。
Service: ビジネスロジックの中心。トランザクション単位。複数の Repository や外部 API を組み合わせる。
Repository: DB との対話。クエリのみ。ビジネス判断はしない。
なぜこのように分けるのか
1. 責務の分離: 各層が一つのことだけをうまく行います。Controller が DB クエリも担うと、混乱した設計になります。
2. テスト容易性: Service をテストする際、実際の Controller や Repository なしに単体テストが可能。モックに差し替えられます。
3. 変更の隔離: HTTP が GraphQL に変わっても、修正するのはController だけ。Service と Repository はそのまま。
4. トランザクション境界: @Transactional はService メソッドにのみ付けるのが標準。Controller や Repository に付けると曖昧な境界になります。
実践例
Controller は薄く、Service はビジネスルールで厚く、Repository はDB アクセスのみ — すっきりした構造です。
DTO — 層間のデータ運搬オブジェクト
各層は同じオブジェクトを直接やり取りしません。DTO (Data Transfer Object) に変換して受け渡します。
- ▸CreateUserDto — リクエストボディ
- ▸User — JPA エンティティ(DB マッピング)
- ▸UserDto — レスポンスオブジェクト
なぜ分けるのか?
- ▸エンティティの内部構造が外部に露出しない(セキュリティ・カプセル化)
- ▸パスワードのようなセンシティブなフィールドをレスポンスから自然に除外できる
- ▸API の変更がエンティティに影響しない(疎結合)
Java 14+ の record は DTO 作成に最適です:
よくあるアンチパターン
1. Controller がビジネスロジックを持つ: トランザクションが壊れ、再利用できない
2. Service が HttpServletRequest を受け取る: HTTP 依存となりテストと再利用が困難
3. Repository にビジネスルール: 同じクエリを別の場所にも書くことになる
4. エンティティをそのままレスポンスに返す: パスワード漏洩・内部構造が外部と結合
まとめ
3層アーキテクチャはSpring の標準です。各層が一つのことだけをうまく行えるよう責務を分けます。DTO で層間のデータ運搬を行うことで結合度が下がります。
🤖 AI にこう頼んでみましょう
このレッスンの概念を知っていれば、AI に具体的に指示できます。漠然とした「直して」ではなく、語彙を持ったリクエスト — それがトークン節約の出発点です。
- ▸「この Spring Boot コードに Layered Architecture — 責務をレイヤーで分ける パターンを適用して」
- ▸「Layered Architecture — 責務をレイヤーで分ける に関連した
@SpringBootTest統合テストを書いて」 - ▸「実務で Layered Architecture — 責務をレイヤーで分ける を使う際に注意すべき落とし穴を3つ教えて」
なぜトークンが節約できるのか
概念を知らないと、AI の回答を受け取っても「それって何ですか?」と再度聞かなければなりません。その「再質問」がトークンを消費します。概念を一度学んでおけば、会話が一度で終わります。