ひとことで言うと#
ビジネスロジック(ドメイン)を中心に置き、依存関係を常に外側から内側へ向けることで、フレームワークやDBの変更に振り回されない堅牢なシステムを作るアーキテクチャ。同心円の図で有名。
押さえておきたい用語#
- 依存性逆転の原則(Dependency Inversion Principle)
- 上位モジュール(ビジネスロジック)が下位モジュール(DB・フレームワーク)に直接依存せず、インターフェースを介して依存させる設計原則のこと。クリーンアーキテクチャの根幹をなす考え方。
- エンティティ(Entity)
- アプリケーション全体で共通のビジネスルールの核心となるオブジェクトのこと。外部の仕組みに一切依存せず、同心円の最も内側に位置する。
- ユースケース(Use Case)
- 「ユーザーが商品を注文する」のようなアプリケーション固有のビジネスロジックを記述する層のこと。エンティティを使ってビジネスルールを実行する。
- インターフェースアダプター(Interface Adapter)
- Controller、Presenter、Gatewayなど、外部と内部をつなぐ変換層のこと。外部のデータ形式と内部のデータ形式を相互に変換する。
- 境界(Boundary)
- 各層の間に設けたインターフェースの壁のこと。この壁により依存関係が常に内側に向かい、外側の変更が内側に影響しない構造を実現する。
クリーンアーキテクチャの全体像#
こんな悩みに効く#
- フレームワークを変えたいのに、ビジネスロジックと密結合で手が出せない
- テストを書きたいのに、DBやAPIへの依存が多くてモックだらけになる
- コードがどこに何を書けばいいかわからず、どんどんスパゲッティ化する
基本の使い方#
ビジネスルールの核心となるオブジェクトを定義する。
- アプリケーション全体で共通のビジネスルールを表現する
- 外部の仕組み(DB、UI、フレームワーク)に一切依存しない
- 例:「注文は合計金額が0円以上でなければならない」というルール
ポイント: ここが同心円の最も内側。最も変更されにくく、最も重要な層。
アプリケーション固有のビジネスロジックを記述する。
- 「ユーザーが商品を注文する」のようなアプリの振る舞いを定義
- Entityを使ってビジネスルールを実行する
- 入出力のインターフェースを定義し、外側の層に実装を任せる
ポイント: 「このアプリで何ができるか」をここに集約する。UIやDBの話はしない。
外部と内部をつなぐ変換層を作る。
- Controller、Presenter、Gatewayなどがここに入る
- 外部のデータ形式(JSON、DBレコード)と内部のデータ形式を変換する
- リポジトリパターンの実装もこの層
ポイント: ユースケース層が定義したインターフェースを、ここで具体的に実装する。
フレームワーク、DB、Web、UIなどの具体的な技術を配置する。
- Webフレームワーク、ORMなどの外部ツールはすべてここ
- この層は「つなぎ」のコードだけ書く
- 最も変更されやすいが、内側には影響を与えない
ポイント: フレームワークは「詳細」であり「方針」ではない。いつでも差し替えられる状態を保つ。
具体例#
状況: ECサイト(月間注文数50,000件)がStripeからPayPay決済に移行することになった。
Entity層: Orderクラスに「合計金額が0円以上」「在庫が十分」などのビジネスルールを実装。決済手段には一切依存しない。
Use Case層: PlaceOrderUseCaseでPaymentGatewayインターフェースを定義。決済の具体実装は外側に任せる。
Adapter層: StripePaymentGatewayをPayPayPaymentGatewayに差し替え。Use Case層のコードは1行も変更不要。
結果: 決済基盤の移行にかかった期間はAdapter層の実装2日のみ。クリーンアーキテクチャを適用していなかった旧システムでは同様の移行に3週間を要していた。移行コストを85%削減できた。
状況: ユーザー数10万人のSaaSプロダクト。MySQL 5.7のEOLに伴い、PostgreSQLへの移行を決定。
Entity層・Use Case層: ビジネスロジックにSQL固有の記述は一切なし。「ユーザーを検索する」「レポートを生成する」などのユースケースは変更不要。
Adapter層: MySQLUserRepositoryをPostgresUserRepositoryに差し替え。同じUserRepositoryインターフェースを実装するだけ。
テスト: Use Case層のテストはMockリポジトリでDBに依存しないため、移行前後で全462件のテストが変更なしで通過。
結果: DB移行で変更が必要だったのはリポジトリ実装の12ファイルのみ。ビジネスロジック側の変更はゼロ。
状況: 5年間運用してきたモノリスアプリ。注文・在庫・通知をマイクロサービスに分離したい。
クリーンアーキテクチャの恩恵: ユースケース層で定義したInventoryServiceインターフェースの実装を、同一プロセス内の呼び出しからHTTP/gRPCクライアントに差し替えるだけ。
段階的移行:
- 在庫サービスを独立デプロイ →
LocalInventoryServiceをRemoteInventoryServiceに変更 - 通知サービスを独立デプロイ → 同様にAdapter層のみ変更
- 注文サービスをモノリスから分離
結果: 各サービスの分離にかかった平均期間は1サービスあたり1週間。Use Case層のコード変更は合計でわずか23行。依存逆転の原則が分離の柔軟性を担保した。
やりがちな失敗パターン#
- 層を飛び越えて依存する — ユースケース層から直接DBドライバを呼んでしまうと、テスト困難で変更にも弱くなる。依存は必ずインターフェースを通すこと
- 過剰な設計で小さいプロジェクトに適用する — 個人の小さなツールにクリーンアーキテクチャをフル適用すると、ファイル数が爆発して本末転倒。プロジェクト規模に合わせて適用度合いを調整する
- データの受け渡しが冗長になる — 層をまたぐたびにDTOを作りすぎて、同じようなクラスが乱立する。本当に必要な変換だけに絞ること
- 「依存は内側に向ける」を誤解する — 内側の層が外側の具体的なクラスをimportしていないか形式的にチェックするだけで、設計思想を理解しない。「なぜ内側に向けるのか」をチーム全体で共有すること
まとめ#
クリーンアーキテクチャは 「ビジネスロジックを守る」 ための設計手法。依存関係を内側に向けるというシンプルな原則を守るだけで、テストしやすく、変更に強いシステムが作れる。ただし万能薬ではなく、プロジェクト規模に合わせた適用が大事。まずはEntity層とUse Case層の分離から始めてみよう。