メインコンテンツまでスキップ

フレームワーク設計原則 - CoC, DRY, SoC

このドキュメントの目的

フレームワークが採用している設計原則を理解し、なぜそのような設計になっているかを学びます。


目次

  1. Convention over Configuration (CoC)
  2. Don't Repeat Yourself (DRY)
  3. Separation of Concerns (SoC)
  4. まとめ

Convention over Configuration (CoC)

「設定地獄」という問題

フレームワークを使う前に、「設定地獄」という問題を理解する。

┌─────────────────────────────────────────────────────────────┐
│ 設定地獄とは │
├─────────────────────────────────────────────────────────────┤
│ │
│ フレームワーク: 「何でもできます!柔軟です!」 │
│ │
│ 開発者: 「じゃあ使ってみよう」 │
│ │
│ フレームワーク: │
│ 「まず設定ファイルを書いてください」 │
│ 「データベースの接続先は?」 │
│ 「URLとコードの対応は?」 │
│ 「ログの出力先は?」 │
│ 「セッションの保存場所は?」 │
│ 「キャッシュの設定は?」 │
│ 「...」 │
│ │
│ 開発者: 「Hello Worldするだけなのに100行の設定...」 │
│ │
└─────────────────────────────────────────────────────────────┘

CoCの考え方

┌─────────────────────────────────────────────────────────────┐
│ CoC = Convention over Configuration │
│ (設定より規約) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 基本的な考え方: │
│ │
│ 「ほとんどの人が同じ設定をするなら、 │
│ それをデフォルトにしてしまおう」 │
│ │
│ 「明示的な設定がなければ、規約(慣例)に従う」 │
│ │
└─────────────────────────────────────────────────────────────┘

日常での例え

┌─────────────────────────────────────────────────────────────┐
│ CoCを日常で例えると │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【CoCなし: 毎回全て指定】 │
│ │
│ コーヒーショップで: │
│ 店員「サイズは?」 │
│ 客 「Mで」 │
│ 店員「温度は?」 │
│ 客 「ホットで」 │
│ 店員「カップは紙?陶器?」 │
│ 客 「紙で」 │
│ 店員「砂糖は何グラム?」 │
│ 客 「普通で...」 │
│ 店員「ミルクの種類は?量は?」 │
│ 客 「もういいよ!普通のコーヒーをくれ!」 │
│ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【CoCあり: デフォルトがある】 │
│ │
│ コーヒーショップで: │
│ 客 「コーヒーください」 │
│ 店員「はい、どうぞ」(Mサイズ、ホット、紙カップ) │
│ │
│ 客 「アイスのLサイズで」 │
│ 店員「はい、どうぞ」(変えたい部分だけ指定すればOK) │
│ │
│ 規約(デフォルト)があるから、 │
│ 変えたい部分だけ指定すればいい │
│ │
└─────────────────────────────────────────────────────────────┘

CoCの具体例

┌─────────────────────────────────────────────────────────────┐
│ フレームワークでのCoC例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. ディレクトリ構造の規約 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 「設定ファイルはここに置く」という規約があれば │ │
│ │ どこに何があるか迷わない │ │
│ │ │ │
│ │ src/ │ │
│ │ ├── controllers/ ← コントローラはここ │ │
│ │ ├── services/ ← サービスはここ │ │
│ │ ├── repositories/ ← リポジトリはここ │ │
│ │ └── config/ ← 設定はここ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 2. 命名規約 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 「UserController は /users を担当」 │ │
│ │ 「UserRepository は users テーブルを担当」 │ │
│ │ │ │
│ │ → 名前から役割が分かる │ │
│ │ → マッピング設定が不要 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 3. デフォルト値 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 「ポート番号は8080」 │ │
│ │ 「タイムアウトは30秒」 │ │
│ │ 「文字コードはUTF-8」 │ │
│ │ │ │
│ │ → 大多数が使う値をデフォルトに │ │
│ │ → 変えたいときだけ設定 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

CoCのトレードオフ

┌─────────────────────────────────────────────────────────────┐
│ CoCのトレードオフ │
├─────────────────────────────────────────────────────────────┤
│ │
│ メリット: │
│ ├── 設定ファイルが減る → コードがシンプルに │
│ ├── 新しいプロジェクトをすぐ始められる │
│ ├── チーム内で構造が統一される │
│ └── 「どこに何を置くか」で迷わない │
│ │
│ デメリット: │
│ ├── 規約を知らないと「魔法」に見える │
│ ├── 規約から外れたいときに面倒 │
│ ├── 学習コストがある(規約を覚える必要) │
│ └── 暗黙的すぎてデバッグしづらい場合がある │
│ │
│ 重要: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 規約はオーバーライド可能 │ │
│ │ │ │
│ │ 「規約に従うと楽、でも強制ではない」 │ │
│ │ 「必要なときは明示的に設定できる」 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

idp-server での実例

┌─────────────────────────────────────────────────────────────┐
│ idp-server でのCoC │
├─────────────────────────────────────────────────────────────┤
│ │
│ idp-server では「明示性」を重視し、CoCを控えめに使用 │
│ │
│ 採用しているCoC: │
│ ├── ディレクトリ構造の規約 │
│ │ └→ handler/, service/, repository/ の配置 │
│ ├── 命名規約 │
│ │ └→ XxxHandler, XxxService, XxxRepository │
│ └── 設定ファイルの場所 │
│ └→ application.yml │
│ │
│ 採用していないCoC: │
│ ├── 自動コンポーネントスキャン │
│ │ └→ 明示的にBean登録(何が登録されるか明確に) │
│ └── 暗黙的なDI │
│ └→ コンストラクタで明示的に依存を宣言 │
│ │
│ 理由: │
│ 「魔法」を減らし、コードを読めば動作が分かるようにする │
│ │
└─────────────────────────────────────────────────────────────┘

Don't Repeat Yourself (DRY)

繰り返しの問題

┌─────────────────────────────────────────────────────────────┐
│ 繰り返しの何が問題か │
├─────────────────────────────────────────────────────────────┤
│ │
│ 同じ「知識」が複数の場所にあると... │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 場所A: 「メールは@を含み、5文字以上」 │ │
│ │ 場所B: 「メールは@を含み、5文字以上」 │ │
│ │ 場所C: 「メールは@を含み、5文字以上」 │ │
│ │ │ │
│ │ ある日、ルール変更: │ │
│ │ 「メールは@を含み、3文字以上に変更」 │ │
│ │ │ │
│ │ → 場所Aを修正 │ │
│ │ → 場所Bを修正し忘れ │ │
│ │ → 場所Cの存在を知らない │ │
│ │ │ │
│ │ 結果: システム内で矛盾が発生 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

DRYの本質

┌─────────────────────────────────────────────────────────────┐
│ DRYの本質 │
├─────────────────────────────────────────────────────────────┤
│ │
│ DRY = Don't Repeat Yourself │
│ │
│ "Every piece of knowledge must have a single, │
│ unambiguous, authoritative representation │
│ within a system." │
│ │
│ 「すべての知識はシステム内で唯一の、明確な、 │
│ 正式な表現を持たなければならない」 │
│ │
│ 重要: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ DRYは「コードの重複」だけの話ではない │ │
│ │ │ │
│ │ 「知識」「ルール」「意図」の重複を避ける │ │
│ │ │ │
│ │ ・ビジネスルール │ │
│ │ ・設定値 │ │
│ │ ・データ構造の定義 │ │
│ │ ・アルゴリズム │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

DRYの日常での例え

┌─────────────────────────────────────────────────────────────┐
│ DRYを日常で例えると │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【DRY違反: 住所を複数箇所に記載】 │
│ │
│ 会員カード: 東京都渋谷区... │
│ 配送先登録: 東京都渋谷区... │
│ 請求書送付先: 東京都渋谷区... │
│ │
│ 引っ越したら? │
│ → 3箇所とも変更が必要 │
│ → 1箇所変更し忘れると届かない │
│ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【DRY適用: 住所は1箇所で管理】 │
│ │
│ マイページ: 住所 = 東京都渋谷区... │
│ 会員カード: → マイページの住所を参照 │
│ 配送先: → マイページの住所を参照 │
│ 請求書送付先: → マイページの住所を参照 │
│ │
│ 引っ越したら? │
│ → マイページの住所を1回変更するだけ │
│ → 全てに自動で反映 │
│ │
└─────────────────────────────────────────────────────────────┘

DRYの適用例

┌─────────────────────────────────────────────────────────────┐
│ DRYの適用例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 定数の一元管理 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ✗ 各所に「30000」と書く │ │
│ │ ○ TIMEOUT_MS = 30000 を1箇所で定義、参照する │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 2. バリデーションルールの一元管理 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ✗ 各Controllerでメール検証ロジックを書く │ │
│ │ ○ EmailValidator を1つ作り、各所から呼ぶ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 3. データ構造の一元管理 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ✗ API仕様書とコードで別々にフィールド定義 │ │
│ │ ○ コードから仕様書を自動生成(Single Source) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

過度なDRYの罠

┌─────────────────────────────────────────────────────────────┐
│ 過度なDRYは逆効果 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 「似ているコード」は必ずしも「同じ知識」ではない │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ UserService と OrderService に似たコードがある │ │
│ │ │ │
│ │ → 無理に共通化 │ │
│ │ → 条件分岐だらけの複雑なコードに │ │
│ │ → 片方を変えると他方に影響 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 判断基準: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 「この2つは同じ理由で変更されるか?」 │ │
│ │ │ │
│ │ Yes → 共通化すべき(同じ知識) │ │
│ │ No → 別々のままでよい(偶然似ているだけ) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 「2回書いてから共通化を検討」という指針もある │
│ │
└─────────────────────────────────────────────────────────────┘

idp-server での実例

┌─────────────────────────────────────────────────────────────┐
│ idp-server でのDRY │
├─────────────────────────────────────────────────────────────┤
│ │
│ DRYを適用している箇所: │
│ │
│ 1. OAuthエラーコードの一元管理 │
│ └→ エラーコード定数を1箇所で定義 │
│ └→ 仕様変更時に1箇所修正で済む │
│ │
│ 2. バリデーションロジック │
│ └→ Validator/Verifier を共通コンポーネント化 │
│ └→ 各Handlerから呼び出し │
│ │
│ 3. レスポンス生成 │
│ └→ ResponseCreator で統一 │
│ └→ 形式変更時に1箇所修正で済む │
│ │
│ DRYを適用していない箇所(意図的): │
│ │
│ 各エンドポイントのHandler │
│ └→ 似ているが「変更理由が異なる」ため別々に │
│ └→ 認可エンドポイントとトークンエンドポイントは │
│ 仕様上別物なので、無理に共通化しない │
│ │
└─────────────────────────────────────────────────────────────┘

Separation of Concerns (SoC)

「関心」とは何か

┌─────────────────────────────────────────────────────────────┐
│ 「関心」とは? │
├─────────────────────────────────────────────────────────────┤
│ │
│ 関心(Concern)= プログラムが扱う「何か」 │
│ = 「気にすべきこと」「責任範囲」 │
│ │
│ Webアプリケーションの関心事の例: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ・HTTPリクエストの受け取り │ │
│ │ ・入力データの検証 │ │
│ │ ・ビジネスロジック(業務処理) │ │
│ │ ・データの保存・取得 │ │
│ │ ・認証・認可 │ │
│ │ ・ログ出力 │ │
│ │ ・エラーハンドリング │ │
│ │ ・レスポンスの生成 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

なぜ分離が必要か

┌─────────────────────────────────────────────────────────────┐
│ 関心が混在する問題 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 全ての関心が1つの場所に混在していると... │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ createUser() { │ │
│ │ // HTTPの関心 │ │
│ │ name = request.getParameter("name") │ │
│ │ │ │
│ │ // バリデーションの関心 │ │
│ │ if (name == null) throw error │ │
│ │ │ │
│ │ // 認可の関心 │ │
│ │ if (!user.isAdmin()) throw error │ │
│ │ │ │
│ │ // ビジネスロジックの関心 │ │
│ │ user = new User(name) │ │
│ │ │ │
│ │ // データアクセスの関心 │ │
│ │ connection = getConnection() │ │
│ │ statement = connection.prepare("INSERT...") │ │
│ │ statement.execute() │ │
│ │ │ │
│ │ // ログの関心 │ │
│ │ logger.info("User created") │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 問題: │
│ ├── コードが長く、読みづらい │
│ ├── テストが困難(DBがないとテストできない) │
│ ├── 変更が影響しやすい(認可を変えたいだけなのに...) │
│ └── 再利用できない(このロジックを別の場所で使いたい) │
│ │
└─────────────────────────────────────────────────────────────┘

SoCの考え方

┌─────────────────────────────────────────────────────────────┐
│ SoC = Separation of Concerns │
│ (関心の分離) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 基本的な考え方: │
│ │
│ 「異なる関心事は、異なる場所に置く」 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ HTTPの関心 → Controller に │ │
│ │ ビジネスの関心 → Service に │ │
│ │ データの関心 → Repository に │ │
│ │ │ │
│ │ それぞれが「自分の責任範囲」だけを担当 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

日常での例え

┌─────────────────────────────────────────────────────────────┐
│ SoCを日常で例えると │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【SoCなし: 1人で全部やる】 │
│ │
│ レストランのオーナーが1人で: │
│ ・予約受付 │
│ ・接客 │
│ ・調理 │
│ ・会計 │
│ ・清掃 │
│ ・経理 │
│ │
│ → 全部覚えるのが大変 │
│ → 1つの問題が全てに影響 │
│ → 休めない │
│ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【SoCあり: 役割分担】 │
│ │
│ ホールスタッフ: 予約・接客・会計 │
│ シェフ: 調理 │
│ 経理担当: 経理 │
│ 清掃スタッフ: 清掃 │
│ │
│ → 各自が専門分野に集中できる │
│ → シェフが変わっても接客には影響しない │
│ → 人を入れ替えやすい │
│ │
└─────────────────────────────────────────────────────────────┘

レイヤー分離(縦の分離)

┌─────────────────────────────────────────────────────────────┐
│ レイヤー分離(縦の分離) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 関心を「層(レイヤー)」として分離する │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Presentation Layer(表示層) │ │
│ │ │ │
│ │ 関心: HTTP、画面表示、入力受付 │ │
│ │ 知らないこと: DBの種類、ビジネスルールの詳細 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ 呼び出し │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Application Layer(アプリケーション層) │ │
│ │ │ │
│ │ 関心: ユースケースの実行、トランザクション境界 │ │
│ │ 知らないこと: HTTP、画面、DBの詳細 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ 呼び出し │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Domain Layer(ドメイン層) │ │
│ │ │ │
│ │ 関心: ビジネスルール、ドメインロジック │ │
│ │ 知らないこと: HTTP、DB、フレームワーク │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ 呼び出し │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Infrastructure Layer(インフラ層) │ │
│ │ │ │
│ │ 関心: 技術的詳細、外部システム連携 │ │
│ │ 知らないこと: ビジネスルール │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 原則: 上の層は下の層を知っている、下の層は上の層を知らない │
│ │
└─────────────────────────────────────────────────────────────┘

横断的関心事

┌─────────────────────────────────────────────────────────────┐
│ 横断的関心事 │
├─────────────────────────────────────────────────────────────┤
│ │
│ レイヤーを「横断」して存在する関心事がある │
│ │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │Presen- │ │Applica-│ │ Domain │ │Infra- │ │
│ │tation │ │tion │ │ │ │structure│ │
│ │ │ │ │ │ │ │ │ │
│ │ × │ │ × │ │ × │ │ × │ ← ログ │
│ │ × │ │ × │ │ × │ │ × │ ← 認証 │
│ │ × │ │ × │ │ × │ │ × │ ← 監査 │
│ │ │ │ │ │ │ │ │ │
│ └────────┘ └────────┘ └────────┘ └────────┘ │
│ │
│ これを各レイヤーに書くと: │
│ ├── コードが散らばる │
│ ├── DRY違反 │
│ └── 変更が大変 │
│ │
│ 解決策: │
│ ├── 共通コンポーネントとして分離 │
│ ├── インターセプター/ミドルウェア │
│ └── AOP(アスペクト指向プログラミング) │
│ │
└─────────────────────────────────────────────────────────────┘

SoCのメリット

┌─────────────────────────────────────────────────────────────┐
│ SoCのメリット │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 理解しやすい │
│ ┌───────────────────────────────────────────────────┐ │
│ │ 各コンポーネントが「1つのこと」だけを担当 │ │
│ │ → 読むべき範囲が限定される │ │
│ │ → 新しいメンバーも理解しやすい │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ 2. 変更しやすい │
│ ┌───────────────────────────────────────────────────┐ │
│ │ DBを変えたい → Repositoryだけ変更 │ │
│ │ UIを変えたい → Controllerだけ変更 │ │
│ │ → 影響範囲が限定される │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ 3. テストしやすい │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Serviceのテスト → Repositoryをモックに │ │
│ │ → DBなしでビジネスロジックをテストできる │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ 4. 再利用しやすい │
│ ┌───────────────────────────────────────────────────┐ │
│ │ 同じServiceを複数のControllerから使える │ │
│ │ → コードの重複を避けられる │ │
│ └───────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

idp-server での実例

┌─────────────────────────────────────────────────────────────┐
│ idp-server でのSoC │
├─────────────────────────────────────────────────────────────┤
│ │
│ idp-server のレイヤー構造: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Controller(Presentation) │ │
│ │ └→ HTTPリクエスト/レスポンスの変換だけ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ EntryService / UseCase(Application) │ │
│ │ └→ ユースケースの組み立て、トランザクション境界 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Handler / Service(Domain) │ │
│ │ └→ OAuth/OIDC仕様に基づくビジネスロジック │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Repository / Gateway(Infrastructure) │ │
│ │ └→ DBアクセス、外部API呼び出し │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 横断的関心事の分離: │
│ ├── Validator/Verifier: 検証ロジックを独立 │
│ ├── ContextCreator: コンテキスト生成を独立 │
│ └── ResponseCreator: レスポンス生成を独立 │
│ │
└─────────────────────────────────────────────────────────────┘

まとめ

3つの原則の関係

┌─────────────────────────────────────────────────────────────┐
│ 3つの原則の関係 │
├─────────────────────────────────────────────────────────────┤
│ │
│ SoC(関心の分離) │
│ │ │
│ │ 「異なる関心事は分離する」 │
│ │ → 分離すると、各部分がシンプルになる │
│ │ │
│ ↓ │
│ DRY(繰り返しの排除) │
│ │ │
│ │ 「同じ知識は1箇所にまとめる」 │
│ │ → 分離した部品の中で、共通部分を抽出 │
│ │ │
│ ↓ │
│ CoC(設定より規約) │
│ │ │
│ │ 「よくある設定はデフォルトにする」 │
│ │ → 繰り返し必要な設定を規約化 │
│ │ │
│ ↓ │
│ 結果: シンプルで保守しやすいコード │
│ │
└─────────────────────────────────────────────────────────────┘

覚えておくべきこと

原則意味問題解決策
CoC設定より規約設定が多すぎるデフォルトを用意
DRY繰り返さない同じ知識が複数箇所に1箇所にまとめる
SoC関心の分離異なる関心が混在レイヤー/コンポーネントに分離

適用の指針

┌─────────────────────────────────────────────────────────────┐
│ 適用の指針 │
├─────────────────────────────────────────────────────────────┤
│ │
│ これらの原則は「銀の弾丸」ではない │
│ │
│ ✓ 状況に応じて適用する │
│ └→ 小規模なら厳密に分離しなくてもよい │
│ │
│ ✓ トレードオフを理解する │
│ └→ CoCは学習コストが上がる │
│ └→ 過度なDRYは複雑さを増す │
│ └→ 過度なSoCはファイル数が増える │
│ │
│ ✓ 「なぜ」を理解する │
│ └→ 原則を守ることが目的ではない │
│ └→ 保守しやすいコードを書くことが目的 │
│ │
└─────────────────────────────────────────────────────────────┘

次のステップ


練習問題

Q1: CoCの例を挙げてください

あなたが使っているフレームワークや言語で、「設定を書かなくても動く」例を挙げてください。

解答例
  • ディレクトリ構造: 特定のフォルダにファイルを置くと自動で認識される
  • 命名規約: 名前のパターンで役割が決まる(XxxController, XxxService等)
  • デフォルト値: ポート番号、タイムアウト、文字コードなど
  • 自動検出: 特定のアノテーションやマーカーで自動登録

Q2: このコードのDRY違反を指摘してください

場所A: if (age < 0 || age > 150) { error("Invalid age") }
場所B: if (age < 0 || age > 150) { error("年齢が不正") }
場所C: if (userAge < 0 || userAge > 150) { error("Age error") }
答えを見る

違反: 「年齢の有効範囲(0〜150)」という知識が3箇所に散在

問題:

  • 上限を120に変更したい場合、3箇所を修正する必要がある
  • エラーメッセージも統一されていない

改善案:

// 知識を1箇所に集約
AgeValidator.validate(age) // 内部で 0〜150 をチェック

Q3: SoCの観点から、このコードの問題を指摘してください

getUserData() {
// HTTP関心
userId = request.getParameter("id")

// 認証関心
if (!session.isLoggedIn()) { redirect("/login") }

// データアクセス関心
connection = database.connect()
user = connection.query("SELECT * FROM users WHERE id = ?", userId)

// 表示関心
html = "<h1>" + user.name + "</h1>"
response.write(html)
}
答えを見る

問題: 1つの関数に4つの関心が混在

  • HTTP(リクエスト/レスポンス)
  • 認証(ログインチェック)
  • データアクセス(DB操作)
  • 表示(HTML生成)

改善の方向:

  • Controller: HTTPの受け取りと返却だけ
  • AuthService: 認証チェック
  • UserRepository: DBアクセス
  • View/Template: HTML生成

各コンポーネントが「1つの関心」だけを担当するように分離する。