Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Metro: The KMP DI Framework You Never Knew You Needed

2025年10月04日に「Dev.to」が公開したITニュース「Metro: The KMP DI Framework You Never Knew You Needed」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

Kotlin Multiplatform開発で、アプリの部品(依存性)を効率的に管理する新しいDIフレームワーク「Metro」が登場した。DaggerやKoinなど既存DIツールの利点を統合し、設定がシンプルで高速だ。アプリの構造を「Graph」として定義し、Androidアプリでの導入からViewModelへの部品注入まで、具体的な使い方を解説している。

ITニュース解説

ソフトウェア開発の世界では、アプリケーションを構成する様々な部品(モジュールやクラスなど)が、互いに協力し合って動作する。このとき、ある部品が別の部品を必要とすることを「依存関係」と呼ぶ。例えば、ユーザーの情報を表示する部品が、データベースから情報を取得する部品を必要とする場合がこれにあたる。このような依存関係を、コードの中で直接的に作成してしまうと、後からデータベースの仕組みを変えたいときに、ユーザー情報表示部品も変更しなければならなくなり、コードの柔軟性が失われたり、テストが難しくなったりする。

ここで「依存性注入(Dependency Injection、DI)」という考え方が登場する。DIは、部品が自分で必要なものを作るのではなく、外部からそれを受け取る(注入される)仕組みのことだ。これにより、部品同士の結びつき(結合度)が緩やかになり、コードが変更しやすくなったり、テストが行いやすくなったりする。例えば、ユーザー情報表示部品がデータベース部品を「受け取る」ようにしておけば、開発中はテスト用の仮のデータベース部品を渡し、本番環境では実際のデータベース部品を渡すといったことが容易になる。

近年注目されている技術に「Kotlin Multiplatform(KMP)」がある。これは、Kotlinというプログラミング言語を使って、AndroidやiOSなど、複数のプラットフォームで共通のコードを書けるようにする技術だ。KMPを使えば、同じロジックをそれぞれのプラットフォーム向けに書き直す手間を省くことができるため、開発効率が大幅に向上する。

しかし、KMP開発でDIを導入しようとすると、これまでAndroid開発で広く使われてきたDIフレームワーク(Dagger2やHiltなど)が使えないという問題に直面することがある。これらのフレームワークは、Javaのクラスを生成したり、Android固有の機能に強く依存していたりするため、KMPの共通コード部分では機能しないのだ。そのため、KMPプロジェクトでは、これらの問題を解決できる新たなDIフレームワークが必要とされてきた。

このような背景から、KMP向けのDIフレームワークとして、いくつかの選択肢が登場してきた。一つは「Koin」というフレームワークで、これは実行時に依存関係を解決する「ランタイムベース」のDIライブラリだ。Daggerのような複雑な設定が不要で、比較的簡単に導入できるため、初心者でもとっつきやすいという特徴がある。Koinでは、必要な部品とその振る舞いを定義し、それらをモジュールとしてまとめてアプリケーションに組み込むことで、すぐに動作するアプリを作成できる。

もう一つは「kotlin-inject」で、こちらはプログラムのコンパイル時に依存関係を解決する「コンパイルタイムベース」のフレームワークだ。Daggerのように@Module@Component@Injectといった概念を使用するが、Daggerよりもコードの記述量が少なく、論理的なオーバーヘッドも小さい。kotlin-injectはDaggerの強力さとHiltのシンプルさを兼ね備えつつ、KMPで利用可能なプラットフォームに依存しないポータビリティを持っている。

そして今回紹介する「Metro」は、これら既存のDIフレームワークの良い点を組み合わせた、比較的新しいKMP対応DIフレームワークだ。Metroは、kotlin-inject、Dagger、Hilt、Koinといった様々なフレームワークの概念を統合しており、まるで全ての良い部分を盛り込んだ「サンデー」のような存在と言える。

Metroの最大の強みは、Koinのようにプロジェクトを素早く立ち上げられる点と、DaggerやHilt、kotlin-injectに慣れた開発者にとって馴染みやすい概念を提供している点にある。また、そのパフォーマンスは既存のフレームワークと同等かそれ以上であることが示されている。さらに、DaggerやHilt、kotlin-injectとの相互運用性も考慮されており、既存のプロジェクトに段階的に導入することも可能だ。

MetroをKMPプロジェクトに統合するには、まずGradleというビルドツールにMetroのプラグインを追加する。Metroの主要な概念として「Graph(グラフ)」がある。これはDaggerの@Componentやkotlin-injectの@Componentに似ており、アプリケーション内でどのように依存関係が初期化され、必要なサービスに注入されるかを定義する開始点となる。例えば、Androidプラットフォーム固有の依存関係が必要な場合、@DependencyGraphアノテーションを使って「AppGraph」のようなグラフを定義する。

Metroでは、@BindingContainerという概念も重要だ。これはDaggerの@Moduleに相当し、関連する依存関係をグループ化するために使う。例えば、ネットワーク通信やデータベースアクセスに関連する部品群をまとめて定義するのに便利だ。これらの@BindingContainerで定義された部品は、後でGraphに組み込まれ、依存関係として利用できるようになる。

アプリケーションの起動時にGraphをインスタンス化する必要があるが、Androidでは通常、Applicationクラスを継承してこの処理を行う。Metroでは、@DependencyGraph.Factoryアノテーションを使って、グラフを生成するためのファクトリメソッドを自動生成できる。このファクトリを使って、アプリケーションのApplicationクラス内でGraphのインスタンスを作成する。

具体的な依存性注入の例として、Jetpack Composeで利用されるViewModelへの注入を考えてみよう。ViewModelは、ユーザーインターフェースの状態を管理し、データアクセスロジックとUIをつなぐ役割を果たす。ViewModelがデータを取得するためにリポジトリ(データアクセスを担当する部品)を必要とする場合、そのリポジトリはDIによってViewModelのコンストラクタに注入される。

Metroでは、@Injectアノテーションを使ってViewModelのコンストラクタに依存関係が必要であることを示す。さらに、@ContributesIntoMap@ViewModelKeyというアノテーションを併用することで、Metroはアプリケーション内の様々なViewModelを効率的に管理し、必要なときに正しいViewModelのインスタンスを生成できるようになる。これらは、特定のキー(ここではViewModelのクラス自体)に対応するViewModelインスタンスを提供するように、グラフに情報を与える役割を持つ。

Jetpack ComposeでこれらのViewModelを利用するには、通常viewModels()というヘルパー関数を使うが、KMP環境ではDIフレームワークと連携させるために少し工夫が必要になる。Metroは、metroViewModel()という独自のComposable関数を提供しており、これを使うことで、MetroのDIグラフを通じて生成されたViewModelをComposeのUIコンポーネントで簡単に利用できる。この関数は、アプリケーションのMainActivityが持つファクトリを通じて、適切なViewModelのインスタンスを取得する。

このように、MetroはKotlin Multiplatformプロジェクトにおいて、DIを効果的に導入するための強力なツールを提供する。既存のDIフレームワークの利点を統合しつつ、KMPの制約に対応することで、開発者はよりクリーンで保守しやすいコードを、複数のプラットフォームで効率的に開発できるようになる。

関連コンテンツ

関連IT用語

関連ITニュース