去年はiOS Clean ArchitectureについてまだMVC,MVP,MVVMで消耗してるの? iOS Clean Architectureについて色々書いてみたのですが、MobileアプリのArchitectureは色々あって何を採択するか結構悩んでいるんじゃないかなと思います。
Clean Architecture自体も学習コストが高くて理解するまで時間がかかるので、色々あるArchitectureの中からどうゆう基準で採択するのが良いかまとめてみることにしました。
書こうと思ったきっかけは、以下に影響されていたりします。
iOSのArchitectureについてはiOSアプリ設計大全集 2016にまとまっているので、それ以外のことについて書きたいと思います。
Clean Architectureについては、クックパッドさんに招待いただいた合同イベント「Cookpad Tech Kitchen #4 〜Cookpad × MoneyForward〜」で発表した
iOS Clean Architecture のすすめもありますので良ければ見てみてください。
比較対象のArchitecture
以下のArchitectureについて書きますが、各Architectureの比較というより部分的な比較をしつつ何を選択するべきか、どうゆう考え方があるかといった紹介になります。
- MVC
- MVP
- MVVM
- Redux
- VIPER
- Clean Architecture
Model層について
Clean Architectureとそれ以外のArchitectureの一番大きな違いはModel層の細分化です。
MVC, MVP, MVVMはいずれもModelはModelのみ、VIPERはInteractor, Entity(DataStoreを図に入れているものもあります)に分かれています。
VIPER | Clean Architecture |
---|---|
Model層を細く細分化するかどうかは、Mobileアプリが複雑なロジックを持つかどうかで判断すれば良いと思います。
Mobileアプリのロジックが複雑であればあるほど、より細分化されているClean Architectureのメリットを享受できます。
Mobile側が複雑なロジックを持たないケースとしては、サーバー側が複雑なロジックを担当するというものも含みます。
一見複雑そうな機能を持つアプリでも、1画面1APIのようなI/F設計をしている場合は、サーバ側がロジック部を担保することになるのでClean ArchitectureのようなModel層の細分化はかなり冗長になると思います。
サーバがほとんどのロジック部を担保する、もしくはロジックがシンプルである場合ではModel層は比較的シンプルな実装で問題なく、Mobile側が複雑なロジックを持つようであればModel層をLayerで分けることを検討しても良いと思います。
Presentation Layer
Presentation Layerだけで見ると、VIPERのRoutingを除けばClean ArchitectureとVIPER,MVPは同じ構成です。
MVVMとMVPの違いは、MVVMだとFRPを使ってViewとModelをbindingするケースが多いということだと思います。
Layerで見れば、PresenterとViewModelの役割に違いはないです。
ViewModelではFRPによる制約が入るのでオレオレ実装になりにくいメリットがありますが、他の画面やModelを介さないEventのハンドリングもViewModel部が担当する必要があるのでその点はPresenterと同様の実装が必要になります。
上記のようなPresenterで扱うようなEventも全てModelで扱うこともできますが、Fat Modelになってしまうためそれが最適かどうかは状況次第だと思います。
Clean ArchitectureでもMVVMで実装することはできますが、ViewとModelのBindingする実装をViewModelとして扱うだけで、EventをハンドリングするPresenterは別途必要になります。
Redux
ここだけ粒度が違うのですがReduxと他のArchitectureの部分比較が難しいので分けました。
Flux Architectureに代表されるReduxですが、Mobileアプリケーションで採択するケースは少ないと思います。
Webの変遷で様々な問題を解決する中で出てきたArchitectureなので、特にMobileを意識しているものではもちろんありません。
全体のステータスを管理する複雑で巨大なシングルトンを作ることは、AppDelegateに何でもアクセスできる便利関数を作ることに近しい行為です。
一方で、SPAのような振る舞いをするアプリケーションであれば、Flux Architectureのメリットを享受できるケースもあると思います。
異なるプラットフォームの良いところを取り入れて活かすのは良い試みだと思いますが、Webで流行ってるArchitectureだからと思考停止で取り入れてみようという考えは良くないです。
DI,Routing
VIPERやClean Architectureは基本的にDIを前提としたArchitectureとなっています。
基本的にViewの遷移は各ViewControllerに依存しますが、そうするとView間に依存性が出て疎結合にならないので、DIを前提とする場合は遷移に関する処理は別のLayerに切り出すことになります。
VIPERのRoutingはこのLayerにあたり、Clean Architectureでは明確に定義されていないですが、VIPERのRoutingに相当するLayerが必要になります。
またClean ArchitectureではLayerが多いので、サンプルプロジェクトではBuilderというDIを行うLayerを設けています。
DIを導入する場合は、Presentation Layer, Domain Layer, Data Layer以外にも上記のようなApplication Layerが必要になります。
DIにはSwinject, TyphoonといったDIのライブラリがありますが、現状は各Layerを結合するDIを扱うにはあまりメリットがないものしかないので、Layered Architectureでライブラリに依存してまで採択するケースは少ないかなと思います。
今のところiOSではイケてるDIコンテナがないので、自前でコンストラクタ実装するのが良さそうです。
No Silver Bullet
Clean Architecutreのような細分化されたLayer間のI/F(protocol)をいざ定義しようと思うと、どうすべきか結構悩むんじゃないかと思います。
しかしそれは各機能の責務を分けて疎結合にすることがそれほど難しいということであり、設計のフェーズがなくなったり概念を理解できれば何も考えずに実装できるようになるわけではありません。
ちゃんと設計をしてチームでコンセンサスを取らないとFatで無秩序なModelができてしまうのと同じように、Clean Architectureも各Layerに責務を限定して疎結合にする意識がなければ、ただLayerを細かく分けただけの無秩序なコードが生まれてしまいます。
色んなことを書きましたが、チームで開発する際には、なぜそのArchitectureにするのかコンテキストを共有してチームメンバーに納得感がある上で開発することが重要です。
プロジェクトによって最適な開発手法は違うので、メンバーと設計を含めたコンテキストを共有する努力を怠らずに、一緒に最適なArchitectureを考えて育てて行くくらいの気持ちを持つことが一番大事かなと思います。
1人で開発をするのであれば基本的に今まで実装してきたことがあるArchitectureで開発するのが早いし、コンテキストもわかっていてメンテナンスもし易いと思いますので、チャレンジするも良し慣れてるもので高速開発するも良しだと思います。