はじめに
Component Coupling(結合度の原則) は「コンポーネント同士をどう依存させるか」を考えるための原則です。
Robert C. Martin(Uncle Bob)は、このために次の 3つの原則を提唱しています。
Component Coupling の3原則
1. ADP: The Acyclic Dependencies Principle
(非循環依存の原則)
- 定義: コンポーネント依存に循環を作ってはいけない。
- 意味: 依存関係は必ず 有向非循環グラフ(DAG) であるべき。循環があるとビルド・リリース・テストが困難になる。
-
例:
- ✅
UI → Domain ← Data
の一方向依存 - ❌
UI ↔ Domain
の相互依存(UI 修正で Domain も巻き込まれる)
- ✅
2. SDP: The Stable Dependencies Principle
(安定依存の原則)
- 定義: 変化しやすいコンポーネントは、変化しにくいコンポーネントに依存すべき。
- 安定性の指標: 多く依存されるほど、そのコンポーネントは「安定」している。
-
例:
- ✅
Domain
(ビジネスルール)は安定しており、UI
やData
が依存する - ❌
Domain
がUI
に依存(不安定な層に引きずられる)
- ✅
3. SAP: The Stable Abstractions Principle
(安定抽象の原則)
- 定義: 安定したコンポーネントは抽象的であるべき。
- 意味: 安定度が高いのに具体実装ばかりだと、変更が困難になる。抽象を増やして柔軟性を保つ。
-
例:
- ✅
Domain
モジュールはインターフェース(Repository 契約や UseCase)中心 - ❌
Domain
が具体的な DB 実装や API 実装を持ってしまう
- ✅
3原則の相互作用
これら3つは組み合わせて効果を発揮します。
- ADP によって循環をなくす → 依存方向がクリアに
- SDP によって不安定なモジュールが安定したものにぶら下がる
- SAP によって安定したモジュールは抽象的になり、柔軟に実装を差し替え可能に
結果として、大規模開発でも壊れにくく拡張可能な構造が生まれます。
実践例(Flutter プロジェクト)
❌ 悪い構成(結合度が高すぎる)
lib/
ui/
user_screen.dart // Domainを直接利用
domain/
user_usecase.dart // UIをimportしている…(循環)
data/
user_api.dart
-
ui
↔domain
が相互依存 → ADP違反 -
domain
がui
に依存 → SDP違反 -
domain
が具象実装を直接呼ぶ → SAP違反
✅ 良い構成(結合度をコントロール)
features/user/
presentation/
user_screen.dart // UseCaseを呼ぶだけ
domain/
user_usecase.dart // Repositoryインターフェースを利用
user_repository.dart (interface)
data/
user_api.dart // Repository実装
user_repository_impl.dart
-
ADP: 依存方向は
presentation → domain ← data
の一方向 - SDP: 不安定な UI/Data が、安定した Domain に依存
- SAP: Domain は抽象インターフェース中心で、実装は Data 側に
依存関係イメージ(Mermaid 図)
Android の例(Gradle モジュール)
// :feature:user:presentation
dependencies {
implementation(project(":feature:user:domain"))
}
// :feature:user:data
dependencies {
implementation(project(":feature:user:domain"))
implementation(project(":core:network"))
}
// :feature:user:domain
// ← 原則として外部依存なし
まとめ
- ADP: 循環依存を避ける → 依存は常に一方向に
- SDP: 不安定なものは安定したものに依存 → UIやDataはDomainに依存
- SAP: 安定したものは抽象的に → Domainは契約中心に
結合度の原則を守ることで、保守性が高く、変更に強いアーキテクチャを構築できます。