Webアプリケーションは、機能追加を重ねるうちに、画面・業務ロジック・データベース処理が絡み合い、少しの修正が思わぬ場所に影響する状態になりがちです。
この問題への代表的な対処法が、クリーンアーキテクチャです。
当社の「Utilities-Lab」もこの考え方で開発しています。
この記事では、考え方の要点と、PythonとFastAPIでの層構成の例、導入時の注意点を説明します。
クリーンアーキテクチャの基本:依存の向きを内側に揃える
クリーンアーキテクチャは、アプリケーションを同心円状の層に分け、依存の向きを必ず外側から内側へそろえる設計です。
内側ほど業務の本質的なルールを持ち、外側ほどフレームワークやデータベースなど、変わりやすい技術的な詳細を持ちます。
内側の層は外側の層を知らないため、Webフレームワークやデータベースを入れ替えても、業務ロジックには手を入れずに済みます。
また、業務ロジックを単体でテストしやすくなります。
4つの層の役割
| 層 | 役割 | 依存してよいもの |
|---|---|---|
| domain(ドメイン層) | エンティティ、値オブジェクト、抽象インターフェースなど、業務の中心となる概念 | 外部ライブラリ・フレームワークに依存しない |
| application(アプリケーション層) | ユースケース(「何をするか」の手順)を表現する | domain層のみ |
| infrastructure(インフラ層) | データベース、外部API、ファイルなど、技術的な実装。 domainのインターフェースを実装する |
domain層と外部ライブラリ |
| presentation(プレゼンテーション層) | FastAPIのルーターや入出力スキーマなど、外部とのやり取りの窓口 | application層、FastAPI、Pydantic |
Python/FastAPIでのディレクトリ構成例
src/
├── domain/
│ ├── entities/ # データクラス
│ ├── value_objects/ # Enumなど不変の値
│ └── interfaces/ # 抽象基底クラス(ABC)
├── application/
│ └── use_cases/ # ユースケース(フレームワーク非依存)
├── infrastructure/ # 外部ライブラリ・APIクライアントの実装
└── presentation/
├── routers/ # FastAPIルーター
└── schemas/ # Pydanticの入出力スキーマ
tests/ # pytestによるテスト
具体例:フォーマット変換の機能
たとえば「JSONをYAMLに変換する」機能を考えます。
- domain:変換対象の形式を表すEnum(JSON/YAML/TOML)と、「変換器」が満たすべきインターフェース(
convert(content, from, to))を定義する。 - application:ユースケースが、インターフェースを通じて変換を呼び出す。
どのライブラリを使うかは知らない。 - infrastructure:PyYAMLなどの外部ライブラリを使い、インターフェースを実装する。
- presentation:FastAPIのルーターがリクエストを受け取り、ユースケースを呼び、結果をJSONで返す。
この構成では、YAMLのライブラリを別のものに変えても、変更はinfrastructure層に閉じます。
ユースケースのテストでは、本物の変換器の代わりに、テスト用のダミーを差し込めます。
導入するメリット
- 変更に強い:データベースやフレームワークの変更が、業務ロジックに波及しにくくなる。
- テストしやすい:業務ロジックが外部に依存しないため、高速な単体テストを書ける。
- 役割が明確:新しい機能を追加するとき、どこに何を書くかが決まっていて、複数人での開発でも迷いにくくなる。
導入時の注意点
小さすぎるアプリには過剰になりうる
層を分けると、ファイル数とコード量が増えます。
数画面で終わる使い捨てのツールなどでは、かえって見通しが悪くなります。
将来の拡張や長期運用が見込まれるかどうかで判断するとよいでしょう。
形だけ真似しない
ディレクトリを分けても、domain層がフレームワークをimportしていたら意味がありません。
「依存の向き」というルールを守れているかを、レビューで確認することが大切です。
抽象化しすぎない
将来使うかもしれないという理由だけで、インターフェースを乱立させるのは避けます。
実際に差し替える可能性や、テストで差し替える必要があるものに絞るのが現実的です。
まとめ
クリーンアーキテクチャの本質は、層の数ではなく、「変わりやすいものと変わりにくいものを分け、依存の向きを一方向にそろえる」ことです。
長く運用するシステムでは、この考え方が変更のしやすさとして効いてきます。
当社でも、小規模から中規模のシステム開発で、規模と運用期間に応じて採用を検討しています。