clean-architecture について
ソフトウェアの設計、レビュー、依存関係管理に使うアーキテクチャフレームワークです。コードの依存をビジネスルールの内側へ向ける原則を中心に、レイヤー、境界、コンポーネント原則、SOLID を扱います。
主な機能
- 01依存関係ルールと同心円レイヤー
- 02エンティティとユースケースのモデル化
- 03インターフェースアダプターとフレームワーク境界
- 04コンポーネントの凝集度と結合度の原則
- 05SOLID 原則
- 06アーキテクチャの診断とスコアリング
活用シーン
- 01新規システムのモジュール境界の設計
- 02ビジネスロジックとフレームワークや DB の依存確認
- 03コンポーネント間の循環依存の特定
- 04クラスやモジュールの SOLID 違反のレビュー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wondelai/ skills --skill clean-architectureインストール後、Claude Code で/clean-architectureを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/adapters-frameworks.mdboundaries.mdcomponent-principles.mddependency-rule.mdentities-use-cases.mdsolid-principles.md詳細
クリーンアーキテクチャのフレームワーク
ビジネスルールをフレームワーク、データベース、デリバリーの仕組みから独立させるための、規律あるソフトウェア構成のアプローチです。システムアーキテクチャの設計、モジュール境界のレビュー、依存関係の管理に関する助言を行う際に、これらの原則を適用してください。
中核となる原則
ソースコードの依存関係は内側、つまり高レベルのポリシーに向かう必要があります。 内側のサークルにあるものは、外側のサークルについて何も知ってはなりません。この単一のルールにより、テスト可能で、フレームワーク、UI、データベース、外部のあらゆる要素から独立したシステムが実現します。重要なのはビジネスルールです。データベース、Webフレームワーク、デリバリーの仕組みは詳細にすぎません。詳細がポリシーに依存していれば、判断を先送りし、実装を置き換え、ビジネスロジックを単独でテストできます。
スコアリング
目標:10/10。 アーキテクチャが満たす「クイック診断」の7項目それぞれに1点を付与し(0~7点)、その後0~10の範囲に換算します。6~7項目を満たす場合は9~10(依存関係ルールが守られ、ビジネスロジックがフレームワークとDBから独立している)、4~5項目の場合は6~8(コアはテスト可能だが、一部の詳細が内側に漏れている)、2~3項目の場合は3~5(フレームワークまたは永続化の仕組みが構造を決めている)、0~1項目の場合は0~2(境界がなく、ビジネスルールがコントローラーとORMモデルに存在する)です。スコア、失敗した診断項目、および各項目の修正に必要な具体的な依存関係の逆転を報告してください。
1. 依存関係ルールと同心円
中核となる概念: アーキテクチャを同心円として構成します。最も内側にエンティティ(エンタープライズのビジネスルール)、その外側にユースケース(アプリケーションのビジネスルール)、さらに外側にインターフェースアダプター、最外側にフレームワークとドライバーを配置します。ソースコードの依存関係は常に内側を向きます。
有効な理由: 高レベルのポリシーが低レベルの詳細に依存しなければ、ビジネスロジックに触れることなく、データベース、Webフレームワーク、APIの形式を切り替えられます。これにより、スタック内で最も変化しやすい部分に対するシステムの耐性が高まります。
主なポイント:
- 内側のサークルでは、外側のサークルに属する名前を参照できません。外部のクラス、関数、変数、データ形式は使用できません。
- 境界を越えるデータは、外側のサークルに決められるのではなく、内側のサークルにとって最も使いやすい形式にする必要があります。
- 依存関係の逆転(インターフェースを内側で定義し、外側で実装すること)が、このルールを徹底する仕組みです。
- サークルの数は固定ではありません。通常は4つですが、ルールは変わりません。
- フレームワークは詳細であり、アーキテクチャではありません。最外側のサークルに配置します。
コードへの適用例:
| コンテキスト | パターン | 例 |
|---|---|---|
| レイヤーの方向 | 内側のサークルでインターフェースを定義し、外側で実装する | ユースケース内のUserRepositoryインターフェース、アダプター内のPostgresUserRepository |
| データの受け渡し | 境界を越えるのはORMエンティティではなくDTO | ユースケースはActiveRecordモデルではなく、UserResponse DTOを返す |
| 依存関係の方向 | インポートの矢印は常に内側を向く | コントローラーはユースケースをインポートする。ユースケースがコントローラーをインポートすることはない |
内側のサークルから外側へのインポートがある場合は、references/dependency-rule.mdを参照してください。そこでは、4つのサークルのコード例、データの受け渡しに関するルール、および問題を修正するための4段階の依存関係逆転手順を確認できます。
2. エンティティとユースケース
中核となる概念: エンティティは、ソフトウェアがなくても存在する、企業全体のビジネスルールをカプセル化します。ユースケースには、エンティティとの間でデータの流れを調整する、アプリケーション固有のルールが含まれます。
効果がある理由: ビジネスが何をするか(エンティティ)と、アプリケーションがそれをどのように調整するか(ユースケース)を分離することで、エンティティを複数のアプリケーションで再利用でき、コアとなるビジネスルールを変更せずにアプリケーションの動作を変更できます。
主なポイント:
- エンティティはデータベースの行ではありません。重要なビジネスルールをカプセル化したオブジェクト、または純粋関数です
- ユースケースはリクエストモデルを受け取り、レスポンスモデルを返します。フレームワークのオブジェクトは決して受け取りません
- 各ユースケースは、単一のアプリケーション操作です(
CreateOrder、ApproveExpense) - インタラクターパターン:ユースケースクラスは入力境界インターフェースを実装し、出力境界インターフェースを呼び出します
- ユースケースへの変更がエンティティに影響することは決してありません。エンティティへの変更は、ユースケースに波及する場合があります
コードへの適用例:
| コンテキスト | パターン | 例 |
|---|---|---|
| エンティティ設計 | 重要なビジネスルール、フレームワークへの依存ゼロ | Order.calculateTotal() は税ルールを適用します。HTTP については何も知りません |
| リクエスト/レスポンス | 境界を越えるシンプルなデータ構造 | CreateOrderRequest { items, customerId } — ORM モデルは使いません |
| 単一責任 | 操作ごとに 1 つのユースケース | PlaceOrder、CancelOrder、RefundOrder を個別のクラスにします |
| インタラクター | 入力ポートを実装し、出力ポートを呼び出します | PlaceOrderInteractor implements PlaceOrderInput |
インタラクターを設計するとき、またはエンティティとユースケースのどちらに含めるべきかを判断するときは、references/entities-use-cases.md を参照してください。リクエスト/レスポンスモデルの例を含む、エンタープライズビジネスルールとアプリケーションビジネスルールの詳しい解説があります。
3. インターフェースアダプターとフレームワーク
中核となる概念: インターフェースアダプターは、ユースケース/エンティティにとって都合のよい形式と、外部機関が必要とする形式との間でデータを変換します。フレームワークとドライバーは最外層であり、外部世界とつなぐグルーコードです。
効果がある理由: Web フレームワーク、ORM、メッセージキューを外側の円に閉じ込めておけば、いずれかを置き換える際の変更は局所的なものになります。データベースは細部であり、Web も細部です。細部はアプリケーションの骨格ではなく、ビジネスルールに対するプラグインであるべきです。
主なポイント:
- コントローラーは HTTP をユースケースの入力に変換し、プレゼンターはユースケースの出力をビューモデルに変換します
- ゲートウェイはユースケースが定義したリポジトリーインターフェースを実装します。内側の円が契約を定義し、外側がそれを満たします
- ビジネスルールは、データが SQL、NoSQL、フラットファイルのどれに保存されているかや、配信が HTTP であるかを知りません
- フレームワークには用心してください。フレームワークは依存させようとします。距離を保ちましょう
コードへの適用例:
| コンテキスト | パターン | 例 |
|---|---|---|
| コントローラー | 配信手段 → ユースケースの入力 | OrderController.create(req) が CreateOrderRequest を構築し、インタラクターを呼び出します |
| プレゼンター | ユースケースの出力 → ビューモデル | OrderPresenter.present(response) が JSON/HTML 用にフォーマットします |
| ゲートウェイ | データベースごとに実装するリポジトリーインターフェース | SqlOrderRepository implements OrderRepository |
| フレームワーク境界 | フレームワークから内側を呼び出し、逆方向には呼び出しません | Express のルートハンドラーがコントローラーを呼び出します。コントローラーが Express をインポートすることはありません |
コントローラー、プレゼンター、ゲートウェイを接続するとき、またはデータベース/Web は細部だと説明するときは、references/adapters-frameworks.md を参照してください。プラグインアーキテクチャと、フレームワークを境界に閉じ込める方法について解説しています。
4. コンポーネントの原則
中核となる概念: コンポーネントはデプロイの単位です。3つの凝集性の原則がコンポーネントに含めるものを規定し、3つの結合性の原則がコンポーネント間の関係を規定します。
有効な理由: 構成が不適切なコンポーネントは、1つの変更によって無関係なコードの再デプロイが必要になる連鎖的な影響を生みます。これらの原則に従うことで、変更が局所化され、リリースを独立させられます。
重要なポイント:
- REP(再利用/リリース等価の原則):コンポーネント内のクラスは、ひとまとまりとしてバージョン管理・リリースできる必要があります
- CCP(共通閉鎖の原則):同じ理由で同じタイミングに変更されるクラスは、まとめて配置します — コンポーネントに対するSRPです
- CRP(共通再利用の原則):ユーザーが使わないクラスに依存することを強制してはいけません
- ADP(非循環依存の原則):コンポーネントグラフに循環があってはいけません — DIPまたは新しいコンポーネントによって循環を解消します
- SDP(安定依存の原則):安定性の方向に依存します
- SAP(安定抽象化の原則):安定したコンポーネントは抽象的にし、不安定なコンポーネントは具体的にします
コードへの適用:
| コンテキスト | パターン | 例 |
|---|---|---|
| コンポーネントのグループ化 | 一緒に変更されるクラスをグループ化する(CCP) | 注文関連のすべてのユースケースを1つのコンポーネントにまとめる |
| 循環の解消 | DIPを適用して依存関係の向きを反転する | インターフェースを新しいコンポーネントに切り出し、循環を解消する |
| 安定性の指標 | 不安定性 I = Ce / (Ca + Ce) | 依存元が多く、依存先がない → Iは0に近く(安定)なる |
クラスをデプロイ可能なコンポーネントにグループ化するときや依存関係の循環を解消するときは、references/component-principles.mdを参照してください。REP、CCP、CRP、ADP、SDP、SAPの各原則を、不安定性の指標とともに詳しく解説しています。
5. SOLIDの原則
中核となる概念: クラスとモジュールのレベルにおける5つの原則 — 単一責任、オープン・クローズド、リスコフの置換、インターフェース分離、依存性逆転 — は、依存関係ルールを実現するための中位レベルの構成要素です。
有効な理由: 各原則は依存関係が破綻する特定のパターンに対処し、コードベースをレガシーコードの悪夢に変えてしまう硬直性、脆弱性、変更困難性を防ぎます。
重要なポイント:
- SRP:モジュールが変更される理由は1つです — 1つのアクターに対応します(「1つのことをする」という意味ではありません)
- OCP:既存のコードを変更するのではなく、新しいコードを追加して振る舞いを拡張します — ストラテジーパターンやプラグインパターンを使います
- LSP:サブタイプは、クライアントに知られることなく、基底インターフェースを通じて利用できなければなりません — 想定外の例外や無視されるメソッドによって違反されます
- ISP:クライアントは使わないメソッドに依存すべきではありません — 肥大化したインターフェースは不要な結合を生みます
- DIP:高水準モジュールと低水準モジュールは、どちらも高水準モジュールが定義する抽象に依存します
コードへの適用:
| コンテキスト | パターン | 例 |
|---|---|---|
| SRP違反 | クラスが複数のアクターに対応する | Employeeが給与(CFO)、レポート(COO)、永続化(CTO)を扱う |
| ストラテジーによるOCP | 新しいクラスを通じて新しい振る舞いを追加する | ShippingStrategyを実装するExpressShippingを追加する。Orderは変更しない |
| LSP違反 | サブタイプが期待される振る舞いを変えてしまう | Square extends RectangleはsetWidth()/setHeight()の契約を破ります |
| ISPの適用 | 肥大化したインターフェースを役割ごとのインターフェースに分割する | 1つのMultiFunctionDeviceではなく、Printer、Scanner、Faxを使う |
| DIPの配線 | 高水準側がインターフェースを定義し、低水準側が実装する | OrderServiceはStripeClientではなくPaymentGatewayに依存する |
特定のクラスにSRP/OCP/LSP/ISP/DIPを適用するときや、違反を診断するときは、references/solid-principles.mdを参照してください。各原則をコード例と、それによって防げる問題とともに詳しく解説しています。
6. 境界と境界の構造
核心となる概念: 境界とは、重要なものと詳細にすぎないものを分ける線であり、ポリモーフィズムによって実現されます。依存関係は内側を指して境界を越え、制御フローはどちらの方向にも境界を越えることがあります。
有効な理由: 境界を設けるたびに、意思決定を先送りしたり、実装を差し替えたりする選択肢が得られます。戦略的な境界の配置によって、システムが何年にもわたって保守しやすいものになるか、苦労するものになるかが決まります。
重要なポイント:
- 完全な境界では両側に相互インターフェースを使い、部分的な境界ではより単純な戦略やファサードを使います
- Humble Object パターン:境界に近いテストしにくい部分と、テストしやすいロジックの部分に境界コードを分割します
- サービスが自動的にアーキテクチャ上の境界になるわけではありません。肥大化した共有データモデルを持つマイクロサービスは、ネットワーク呼び出しを行うモノリスです
- テストは最も隔離されたコンポーネントです。依存関係は内側に向かい、テストに依存するものはありません
- 早すぎる境界の導入にはコストがかかりますが、境界がない場合も同様です。変化しやすい箇所に境界を設けてください
コードへの適用:
| コンテキスト | パターン | 例 |
|---|---|---|
| 完全な境界と部分的な境界 | 相互ポート、または単独のストラテジー | ユースケースで PlaceOrderInput/PlaceOrderOutput を定義します。単純なケースでは ShippingStrategy を使用します |
| Humble Object | テスト可能なロジックとインフラストラクチャを分離します | PresenterLogic(テスト可能)が ViewModel を生成し、View(Humble Object)がそれをレンダリングします |
| プラグインとしての Main | コンポジションルートがシステムを組み立てます | main() がすべての具象実装を接続し、アプリを起動します |
境界をどこに設けるか、完全な境界と部分的な境界のどちらを選ぶか、または Humble Object パターンを適用するかを判断する際は、references/boundaries.md を参照してください。境界としてのサービス、テストの境界、そして究極のプラグインとしての Main についても説明しています。
よくある間違い
| 間違い | 問題が起きる理由 | 対処法 |
|---|---|---|
| ORM がビジネスロジックに漏れ出す | エンティティがスキーマに結合し、DB の変更によってビジネスルールの書き換えが必要になります | ドメインエンティティと永続化モデルを分離し、アダプターレイヤーでマッピングします |
| コントローラーにビジネスルールを記述する | HTTP なしではテストできず、エンドポイント間で重複します | ロジックをユースケースインタラクターに移し、コントローラーは変換と委譲のみを行います |
| フレームワーク優先のアーキテクチャ | フレームワークが構造を決定し、差し替えるには全面的な書き換えが必要になります | フレームワークをプラグインとして扱い、ビジネス上の機能ごとにコードを構成します |
| コンポーネント間の循環依存 | 変更の影響が予測不能に波及し、独立したリリースができません | DIP を適用するか、共有の抽象化コンポーネントを抽出します |
| 機能ごとに巨大なユースケースを1つ作る | 数千行に及ぶ肥大化したオーケストレーターになります | 単一の操作に集中したユースケースに分割します |
| 「単純だから」と境界を省く | 結合が気づかないうちに蓄積し、やがて莫大なコストになります | 変化しやすい箇所に、先回りして境界を設けます |
| マイクロサービスなら自動的に優れたアーキテクチャになると考える | 分散モノリスは、適切に構成されたモノリスよりも悪いものです | サービス内外の両方で依存関係ルールを適用します。サービスはデプロイの境界であり、アーキテクチャ上の境界ではありません |
クイック診断
| 質問 | いいえの場合 | 対応 |
|---|---|---|
| DB、ウェブサーバー、フレームワークなしでビジネスルールをテストできますか? | ルールがインフラストラクチャに結合しています | エンティティとユースケースをインターフェースの背後に切り出し、外側のレイヤーをモックします |
| すべてのソース依存関係が内側を向いていますか? | 依存関係ルールに違反しています | 境界インターフェースを導入し、問題の依存関係を逆転させます |
| ビジネスロジックに触れずにデータベースを交換できますか? | 永続化の詳細が内側に漏れています | リポジトリパターンを使い、アダプターで永続化を分離します |
| ユースケースは受け渡しの仕組みに依存していませんか? | ユースケースが HTTP/CLI/キューを認識しています | ユースケースのシグネチャではプレーンな DTO を使います |
| フレームワークは最外周のサークルに限定されていますか? | フレームワークがアーキテクチャそのものになっています | フレームワークの呼び出しをインターフェースの背後でラップし、端に配置します |
| コンポーネントグラフに循環はありませんか? | 循環依存が存在します | ADP を適用し、DIP または新しいコンポーネントを使ってすべての循環を解消します |
| Main(コンポジションルート)がすべての依存関係を配線していますか? | 内側のサークルで具象クラスがインスタンス化されています | 構築処理を Main に移し、DI またはファクトリーを使います |
さらに読む
ソフトウェアアーキテクチャに関する Robert C. Martin の決定版ガイドに基づいています。
- "Clean Architecture: A Craftsman's Guide to Software Structure and Design" 著:Robert C. Martin
著者について
Robert C. Martin(「Uncle Bob」) は、1970 年からプログラミングに携わっているソフトウェアエンジニアであり、アジャイルマニフェストの創設時の署名者で、Clean Code、The Clean Coder、Clean Architecture、Clean Agile の著者です。彼の SOLID 原則は、オブジェクト指向設計における基礎的な用語であり、彼の著作は、アーキテクチャとは依存関係を管理し、ビジネスルールをインフラストラクチャの詳細から独立させることだと論じています。