概要
モバイルアプリに対してクリーンアーキテクチャを適用した具体例に関する記事が少ないなぁと感じておりました。そのため、本記事では個人的に思うMVVM×クリーンアーキテクチャの詳細を記載します。
前提
-
モバイルアプリ開発におけるアーキテクチャを想定
- 特に、AndroidかつJetpackComposeの使用を想定
- DIの利用も想定
-
APIサーバを持ち通信を行ってデータを取得する
-
アプリ内のローカルなデータストアも利用する
結論
MVVMにクリーンアーキテクチャを組み込んだ図が次のようになることを想定しています。
グルーピングされたものが、パッケージとして分ける想定です。
(矢印は依存方向を示しており、点線はインターフェースに対する具体実装を示しています。)
設計意図
テスタブルなコード
Model内にあるものをinterfaceを使って表現しているため、それぞれのパッケージのテストがしやすくなります。
UIプレビューの表示ができるようになる
JetpackComposeではUIのプレビュー機能がありますが、UIが変な依存関係を持っていることでプレビューがエラーを起こすケースが多々あります。これは Repository層が具体実装に依存するため であるケースがほとんどなのですが、本アーキテクチャではそれをダミーに差し替えやすい構造になっているため、プレビューの表示が簡単になります。
メンテナンスがしやすい
パッケージの粒度を割と細かくしているため、メンテナンスや変更は比較的に行いやすいと思います。
構成要素
UI
言わずもがな。ViewとViewModelによる構成となります。
Domain
ドメインにはUsecaseとIRepository(Repositoryのインターフェース)を定義しています。IResourceとILocalDataStoreをこちらに入れているのは依存性の逆転を行うことで、Domain層が最上位となるようにするためです。
Repository
ここでいうRepositoryとはアプリ内におけるデータストア全般を示しています。つまりアプリ内で扱うデータはここから全て持ってくるようになります。
-
Resource
Repositoryの中における、外部からとってくるデータ を示します。つまり、API通信によるデータを保持するパッケージとなります。 -
LocalDataStore
Repositoryの中における、内部からとってくるデータ を示します。つまり、デバイスで保持する永久的なデータはここからとってきます。
サンプルコード
(更新予定...)
設計におけるよくある疑問
ViewModelとUsecaseのちがいは?
・ViewModelはあくまで画面の操作、文字変換などそのScreen特有のUIに関するロジックと状態管理
・UsecaseはRepositoryから受け取るデータ加工、調整、計算等
Usecaseを作るのは冗長になるだけでは?
冗長になるケースは確かに多いが、分けておくべきだと思います。
- あとから同じUsecaseの内容を使う事が出てきたときに、同じロジックがViewModelにコピペで量産されることになりメンテナンスが大変になるためです。
さらにいうと、冗長なのは実はViewModelの方だと思っています。(後述あり)
ViewModelが複数のUsecaseを使って良いのか?
Yes。必要になるケースがあるので、むしろその時にこのアーキテクチャの強みが生かされると思います。
UsecaseがほかのUsecaseを持ってきてよいのか?
個人的にはNo。やるならば、HogeSeerviceなどさらに上位のものを作ってそれをつかうのが良いと思います。
UsecaaeはRepositoryを複数取ってくるのは良いのか?
Yes。おそらく複数のRepositoryをとってくるケースの方が多い印象です。
DIはパッケージ的にどこで行う?
これは正しいか微妙ですが、自分はuiパッケージ内で行っています。
アーキテクチャ的には分けた方が良い気もしていますが、パッケージの依存方向などを考慮するとより複雑になるため、よりシンプルになるuiで全てのDIを行なっています。
気になっていること
実はViewModelいらない説
これは宣言的UIを使って上記のアーキテクチャに沿って作成すると、ViewModelはほとんどすることがないことに気づくと思います。
ViewModelの責務は"Viewの状態を管理する" と思っていますが、宣言的UIのフレームワークを使う場合は大した実装にはならないため、これを分ける意義があまりないようにも感じています。
こちらの記事で言及されている内容に近いのかなぁと思っています。
Domain層とSerivceの違い
一般的な(?)クリーンアーキテクチャに則るのならば,UsecaseはService層に該当すると思います。つまりDomain層にはEntityが,Service層にはUsecaseが該当すると思いますが、モバイルアプリがビジネス的なDomainを持っているのか? と考えた時にこれはYesとは言えない気がします。
そのため、Android Develop公式においてもDomainの中にはEntityなどが記載されておらず、Usecaseとしてひっくるめられているのかと思っています。
よって、今回紹介したアーキテクチャは、あえてDomainとSrviceを分けることなくDomainと一括りにしてUsecaseを入れています。
まとめ
MVVMでクリーンアーキテクチャによる設計がベターだと思っています。一方で、ViewModelに関しては実はなくても問題なさそうな気はしています。