0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Clean Architecture】DIP: Dependency Inversion Principle(依存関係逆転の原則)

Last updated at Posted at 2025-09-29

はじめに

SOLID 原則の最後を飾るのが DIP(依存関係逆転の原則)
これは「高水準のモジュールを低水準に縛らない」ための考え方で、拡張性・テスト容易性・保守性 を大幅に向上させます。


1. 定義

依存関係逆転の原則(DIP) とは:

高水準モジュールは低水準モジュールに依存すべきではない。両者は抽象に依存すべきである。
抽象は詳細に依存すべきではない。詳細が抽象に依存すべきである。


2. 直感的な例

DIP 違反(具象クラスに直接依存)

class ApiUserRepository {
    fun fetchUser(): String = "user from API"
}

class UserService {
    private val repository = ApiUserRepository()

    fun getUser() = repository.fetchUser()
}
  • UserServiceApiUserRepository に直依存
  • 実装を差し替える(DB保存、キャッシュ利用など)ときに サービス側まで修正が必要

DIP 準拠(抽象に依存)

interface UserRepository {
    fun fetchUser(): String
}

class ApiUserRepository : UserRepository {
    override fun fetchUser() = "user from API"
}

class DbUserRepository : UserRepository {
    override fun fetchUser() = "user from DB"
}

class UserService(private val repository: UserRepository) {
    fun getUser() = repository.fetchUser()
}
  • UserService抽象 UserRepository に依存
  • 実装(API/DB/Mock)は 注入(DI) で切り替え可能

3. Flutter / Android での例

Flutter(DI with Riverpod)

abstract class WeatherRepository {
  Future<String> fetch();
}

class ApiWeatherRepository implements WeatherRepository {
  @override
  Future<String> fetch() async => "Sunny from API";
}

final weatherRepoProvider = Provider<WeatherRepository>((ref) {
  return ApiWeatherRepository();
});

class WeatherService {
  final WeatherRepository repo;
  WeatherService(this.repo);

  Future<String> getWeather() => repo.fetch();
}

→ Provider の切り替えでテスト用リポジトリに差し替え可能。


Android(Kotlin + Hilt)

interface Logger {
    fun log(message: String)
}

class ConsoleLogger @Inject constructor() : Logger {
    override fun log(message: String) = println("Console: $message")
}

class FileLogger @Inject constructor() : Logger {
    override fun log(message: String) { /* ファイル出力 */ }
}

class UserService @Inject constructor(
    private val logger: Logger
) {
    fun createUser(name: String) {
        logger.log("User created: $name")
    }
}

Logger の実装は DI コンテナ(Hilt/Koin)で差し替え可能。


4. DIP のメリット

  • 実装の差し替えが容易(API → DB → キャッシュ)
  • テスト容易性が向上(モックリポジトリを注入可能)
  • 循環依存を防ぐ(高水準が低水準に縛られない)
  • Clean Architecture との親和性が高い

5. Clean Architecture との関係

DIP は Clean Architecture の依存ルール そのものです。

  • 内側(Domain, Application)は 抽象 を持つ
  • 外側(Infrastructure)が 詳細実装 を依存する
  • これにより「内側は外側を知らない」=独立性とテスト性が担保される

6. 実務での適用ポイント

  • インターフェース駆動設計(ポート/アダプター) を徹底
  • テスト時はモック実装 を注入する仕組みを用意
  • 依存方向をレビューでチェック
    → 「上位レイヤーが下位の具象に依存していないか?」
  • 過剰抽象は避ける
    → 変更可能性が高い部分だけインターフェース化する

まとめ

  • DIP = 抽象に依存し、詳細は外側に閉じ込める
  • これにより 拡張性・テスト容易性・保守性 が飛躍的に向上する
  • Clean Architecture の「依存ルール」の基盤

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?