【アーキテクチャ】クリーンアーキテクチャ - j-komatsu/myCheatSheet GitHub Wiki
クリーンアーキテクチャ
1. クリーンアーキテクチャとは?
クリーンアーキテクチャは、ソフトウェアの保守性と拡張性を高めるために設計されたアーキテクチャパターンです。
ソフトウェア設計の原則を組み合わせたもので、以下の点を重視します。
- 依存性の方向: 外側の層が内側の層に依存し、逆はない
- 分離: ビジネスロジック、プレゼンテーション、データアクセスを分離
- テスト容易性: ユニットテストがしやすくなる
初学者向けの説明
クリーンアーキテクチャは、アプリケーションを「層」に分けて設計する方法です。
層を分けることで、コードの役割を明確にし、メンテナンスをしやすくします。
主要な層:
- エンティティ層: ビジネスルールの中核部分(変更が少ない)
- ユースケース層: アプリケーション固有のビジネスルール
- インターフェースアダプタ層: コントローラやAPI、DBとの接続部分
- フレームワーク・インフラ層: フレームワークや外部サービス(DB、UI)
以下の図は、クリーンアーキテクチャの概念を示しています。
graph TD;
A[エンティティ層] -->|依存| B[ユースケース層];
B -->|依存| C[インターフェースアダプタ層];
C -->|依存| D[フレームワーク・インフラ層];
たとえ話で説明する各層の役割
アプリケーションを レストラン にたとえて考えてみましょう。
層 | 役割 | たとえ |
---|---|---|
エンティティ層 | ビジネスルールの中核部分(変更が少ない) | レシピ本: レストランで作る料理の基準となるレシピ。どの店員が作るか、どのキッチンを使うかに関係なく、基本のルールが決まっている。 |
ユースケース層 | アプリケーション固有のビジネスルール | シェフ: レシピを基に、実際の料理の作り方を指示する。例えば、「今日は特別な注文があるので、少しアレンジしよう」といった判断を行う。 |
インターフェースアダプタ層 | コントローラやAPI、DBとの接続部分 | ウェイター: お客様から注文を受け取り(コントローラ)、キッチンに伝え、出来上がった料理を提供する(API)。厨房と客の橋渡しをする役割。 |
フレームワーク・インフラ層 | フレームワークや外部サービス(DB、UI) | 厨房・食材供給業者: シェフが料理を作るために使うキッチン設備や、食材を仕入れる業者。レストランの基盤となる部分で、変更があるとレストラン全体に影響が出る。 |
このように、それぞれの層には役割があり、
- エンティティ層 は「料理のルール(レシピ)」
- ユースケース層 は「そのルールを使ってどう料理を作るか」
- インターフェースアダプタ層 は「客と厨房をつなぐ役割」
- フレームワーク・インフラ層 は「調理環境や食材供給」
と考えると、各層の役割が明確になります。
専門者向けの詳細
1. クリーンアーキテクチャのメリット
- 保守性が向上: 各層が独立しているため、修正の影響を最小限にできる
- テスト容易性: ビジネスロジックがUIやデータストレージから分離されるため、ユニットテストがしやすい
- 依存関係の整理: 外部フレームワークやライブラリの影響を最小限に抑えられる
2. クリーンアーキテクチャのデメリット
- 初期実装のコストが高い: 設計の段階でしっかり分離する必要がある
- 小規模プロジェクトではオーバーエンジニアリングになりやすい
- チームでの理解が必要: すべての開発者がアーキテクチャを理解している必要がある
3. 各層の詳細
層 | 役割 | 依存関係 |
---|---|---|
エンティティ層 | ビジネスルールの中核 | どこにも依存しない |
ユースケース層 | アプリケーションのビジネスロジック | エンティティ層に依存 |
インターフェースアダプタ層 | API、UI、データストアとの接続 | ユースケース層に依存 |
フレームワーク・インフラ層 | フレームワークや外部システム | インターフェースアダプタ層に依存 |
2. コード例
以下は、Pythonでクリーンアーキテクチャを実装する際の例です。
エンティティ層
class User:
def __init__(self, user_id: int, name: str):
self.user_id = user_id
self.name = name
ユースケース層
class UserUseCase:
def __init__(self, user_repository):
self.user_repository = user_repository
def get_user(self, user_id: int):
return self.user_repository.get_by_id(user_id)
インターフェースアダプタ層(リポジトリ)
class UserRepository:
def __init__(self):
self.users = {1: User(1, "Alice"), 2: User(2, "Bob")}
def get_by_id(self, user_id: int):
return self.users.get(user_id, None)
フレームワーク・インフラ層(API)
from flask import Flask, jsonify
app = Flask(__name__)
user_repository = UserRepository()
user_usecase = UserUseCase(user_repository)
@app.route('/user/<int:user_id>')
def get_user(user_id):
user = user_usecase.get_user(user_id)
if user:
return jsonify({"id": user.user_id, "name": user.name})
return jsonify({"error": "User not found"}), 404
if __name__ == '__main__':
app.run(debug=True)
3. まとめ
クリーンアーキテクチャは、長期的なソフトウェア開発のメンテナンス性と拡張性を向上させる強力な手法です。
- 初学者向けには: 各層を分けることが重要
- 専門者向けには: 依存関係の方向性とテストのしやすさを意識する
このアーキテクチャを活用すれば、スケーラブルで保守しやすいアプリケーションを構築できます。
graph TD;
A[Entities] -->|依存| B[Use Cases];
B -->|依存| C[Interface Adapters];
C -->|依存| D[Frameworks & Drivers];