Clean Architecture + DDD + Redux + RxJavaをAndroidでやるときにどこまで分割するか問題
(追記) 本記事,頭のなかを整理しきれていない状況で書いたためよくわからないことになっていますが,Clean ArchitectureやRedux,DDDの優位な点を解説するような記事ではないことをご了承いただけると幸いです.
全体の構成がどうなっているか・モチベーション・pros・cons等については後日別記事にまとめようと考えています.
いま書いているアプリがClean ArchitectureになりそこねたMVPと中途半端なDDDを組み合わせたようなアーキテクチャになっている. このアプリをある程度キチンとClean ArchitectureとDDDに寄せるにあたり,DDDのレイヤ分け(Data/Domain/Presentation)をどこまで厳格にやるかで悩んでいる.
現状
だいたいこんな感じ.
- Data/Domain/Presentationのすみ分けはしていない
- 実装上は意識してる
- いまは大丈夫だけど,他の人が来た時に厄介かもしれない
- どちらにしろちゃんとしたドキュメントあること前提
- Presentation層からDomain/Data層へのアクセスはなるべくDroiduxの
Store
経由 usecase
の代わりにaction
(DroiduxのAction
)- Repositoryへのアクセスはここでやる
- 本家Reduxと違って
Action
はクラスなのでテストもしやすいしいいかな?という感じ(ActionCreator
が存在しない) - 『これからの「設計」の話をしよう』の
UseCase
が近いか
activity
/fragment
パッケージは作らないModule
/Component
(DaggerのDIコンテナ単位)でパッケージを分けてる- Custom Viewsだけ
view
パッケージ切ってるのがちょっと違和感?
- 画面(
Activity
/Fragment
)ごとに3クラス2インタフェースほど余計に作ることになるのが面倒- Presenterに処理を集中させたら結構なメソッド数になりそうだったので.Clean Architectureにならってinput port/output portを分けてる
- Controllerまで作るの冗長すぎる気がするし,よほどデカいモジュールじゃないかぎりは
Activity
/Fragment
に含めちゃっていい気もした
パッケージ構成は以下のとおり:
├action ├adapter ├api ├cache ├entity │ └mapper ├model ├module │ ├main │ │ ├MainActivity.java │ │ ├MainComponent.java │ │ ├MainModule.java │ │ ├MainController.java │ │ ├MainPresenter.java │ │ └MainView.java │ └login │ │ ├LoginActivity.java │ │ ├LoginComponent.java │ │ ├LoginModule.java │ │ ├LoginController.java │ │ ├LoginPresenter.java │ │ └LoginView.java ├reducer ├repository │ └datasource ├util ├view ├App.java ├AppComponent.java ├AppModule.java └RootStore.java
現状だとData層は主にInfra層(データアクセス周り)を抽象化してるだけだなー.
Motivation
- infra層(
datasource
)ではAndroid Frameworkが深く絡んでこないようにしたい - このぐらい分割することができれば,infra以外のテストは「適切な対象に,適切にinteractionできるか」だけでよくなる
- kyobashi.dex #2の資料に載せたコードみたいな感じ
- DIの恩恵によりinteractionする先はすべてMock/Spyになる
- mockへのテストばかりになってくるので不安にはなる
- ややこしいUI周りのテストも「Mockが正しく呼ばれているか」だけになったら素敵じゃないです?
高度に抽象化されたモジュールのユニットテストはインタフェースの実装(モック)に対して処理が適切に移譲されていくかだけの非常に薄いものになる
— いずみん (@izumin5210) January 17, 2016
techbooster.fm #10では「Androidで分割するlogic/domainってどれだよ」「ぜんぶdomainじゃね?」みたいな話になってたけど, AndroidやフロントエンドJavaScriptの世界でなんらかの設計のフレームワークを持ち込むのって 「presentation/interaction logic・storeの状態遷移・infraを切り分ける」のが目的になってくるのかなと思った. そもそもフロントエンドじゃMVW(Model-View-Whatever)はスケールしないのでみたいな話も(ry.
特にBluetoothLeScanner
とかBluetoothGatt
みたいな意味不明なクラス使うなら余計にinfra抽象化しないとテストできなくて死にそうな目に遭う.
Clean ArchitectureてFluxとどう違うねんみたいな話は『DDD - 持続可能な開発を目指す ~ ドメイン・ユースケース駆動(クリーンアーキテクチャ) + 単方向に制限した処理 + FRP - Qiita』に載ってた.
Fluxアーキテクチャとの違いは?
Fluxアーキテクチャの特徴は次の通りだ。ベストプラクティスとされるパターンに命名して確立しただけで、決して革新的なわけではない。
- 全ての処理をイベント駆動に統一した。
- メッセージングを単方向に制限した。
- イベント同士の依存性を管理するのは、Dispatcherというハブが行う。
全体をイベント駆動にする場合は、Fluxアーキテクチャを参考にできる。また、メッセージングや処理を単方向に制限するのは有用だ。ストアへの処理の時系列を整理するハブについては、クリーンアーキテクチャにはUsecaseがあるし、これの方がビジネスロジックに直結していて分かりやすく、色んなインターフェース方式に対する処理を含む点で汎用的だと思う。
とりあえずそんなこんなでClean Architecture + DDD + Redux + RxJavaやってる.
改善案
package分割
『AndroidではMVCよりMVPの方がいいかもしれない - Konifar's WIP』のサンプルアプリのパターン.
├data │ ├cache │ ├entity │ │ └mapper │ ├exception │ ├executor │ └repository │ ├domain │ ├exception │ ├executor │ ├model │ ├repository │ └usecase │ ├presentation │ ├di │ │ ├component │ │ └module │ ├presenter │ ├service │ └view │ ├activity │ ├adapter │ ├component │ ├fragment │ └util └MainApplication.java
pros
- package分けてるだけなのでコストは発生しない
- (module分割に比べると)ファイルツリーがコンパクト
- 1モジュールなのでdependenciesも単純
cons
- package分けてるだけなので制約も発生しない
- packageが違うので一応意識することはするか
- Domain層にData層への参照等は起きうる
module分割
『android10/Android-CleanArchitecture』が採用しているパターン.
基本的にはさっきのやつのdata
/domain
/presentation
が別モジュールになってるだけ.
依存関係はこんな感じになる:
// data/build.gradle dependencies { compile project(':domain') // snip }
// presentation/build.gradle dependencies { compile project(':domain') compile project(':data') // snip }
また,Data moduleはAndroid Library,Domain moduleはJava Libraryになる.
pros
- Data/Domain/Presentation間の依存に制約が発生する
- 「Data -> Presentation」や「Domain -> それ以外」のような参照が発生しなくなる
- 設計に対するきょうせいギプス的な効果
- Domain層へのAndroidフレームワークの混入も防げる
cons
- ファイルツリーがデカくなる
- 新規作成するの大変
- 各moduleの依存管理が大変
- 何も考えずにやるとDon't Repeat Yourselfもクソもない感じになる
gradle/dependencies/dependencies.{gradle,properties}
みたいなので共通化・集中管理?- 秘伝のgradleタスクになる恐れが
- Domain層にAndroid Frameworkに依存するライブラリを含めることができない
- 適切に設計されていれば発生することは少ないか…?
- むしろいい制約になるという説はある
- DataBinding系のAPIは含めたい気もする…
Observable
等を含むbaseLibraryはAndroid Framework非依存なので大丈夫
どうする?
「設計」って「縛りプレイ」じゃないとブレてくると思っているので,module分割方式はすごく魅力的. 一方で,module分割することで跳ね上がるプロジェクト全体の複雑性(主にdependenciesまわり)の問題やファイルツリー肥大化問題は避けられない感じもするので,どうしたものか…と悩んでる.
References
- The Clean Architecture | 8th Light
- AndroidではMVCよりMVPの方がいいかもしれない - Konifar's WIP
- [ Android ] – これからの「設計」の話をしよう – NET BIZ DIV. TECH BLOG
- AndroidオールスターズでClean Architectureについて発表してきた&参考リンク集 - tomoima525's blog
- まだMVC,MVP,MVVMで消耗してるの? iOS Clean Architectureについて - Qiita
- DDD - 持続可能な開発を目指す ~ ドメイン・ユースケース駆動(クリーンアーキテクチャ) + 単方向に制限した処理 + FRP - Qiita
- Clean Architectureで設計してRxJSを使った話
techbooster.fm 第10回 AfterShow「らことやんざむとひつじのAndroid設計を考えたい!話」 | TechBooster
- izumin5210/Droidux
- Example of TestScheduler and TestSubject (in Spock) // Speaker Deck
- 作者: Eric Evans
- 出版社/メーカー: 翔泳社
- 発売日: 2013/11/20
- メディア: Kindle版
- この商品を含むブログ (3件) を見る
実践ドメイン駆動設計 (Object Oriented SELECTION)
- 作者: ヴァーン・ヴァーノン,高木正弘
- 出版社/メーカー: 翔泳社
- 発売日: 2015/03/17
- メディア: 大型本
- この商品を含むブログ (3件) を見る
[改訂版]Android Studioではじめる 簡単Androidアプリ開発
- 作者: 有山圭二
- 出版社/メーカー: 技術評論社
- 発売日: 2016/01/07
- メディア: 大型本
- この商品を含むブログ (1件) を見る