はじめに
アプリケーションを開発しリリースした際に、当初は問題なくても時が経つにつれて綻びが出てきます。
仕様変更や機能追加やリファクタリングができない(後回しにしたまま)、開発に携わる人の理解や嗜好による統一感の喪失などなど。
そうするとこんなことが起きることもしばしば。
- ViewControllerの肥大化
- 責任範囲がぶれたクラスの誕生
- Model/Helper/Libraryが人の解釈に依存するディレクトリ・ファイルの散見
- ViewModelとModelの扱いがごっちゃになっている
こうなるとどんどん作りが複雑化し、気が付くと負の遺産が出来上がっています。
メンバーの理解度を上げる、チーム内でルールを決めるという解決する手もあるのですが
できればもう少し役割がはっきりしていて、共通認識持ちやすいアーキテクチャがないかなーと思っていてたところ…それはありました。
それが__CleanArchitecture__です!!
http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
実際には、下記の記事で存在を知ったのですが、読んだ時はなにこれ素敵やん!と思ったものです。
まだMVC,MVP,MVVMで消耗してるの? iOS Clean Architectureについて
今回は表題にある通りRxSwift + CleanArchitectureを用いてアプリを作ったのでこんな感じで作っていますという話をまとめてみようと思いました。
(Rx部分が2割,CleanArchitectureが8割ぐらいの構成比です)
RxSwift+CleanArchitectureの構成
構成図
イメージ的にはこんな感じです。
Presentation層とDomain層とData層 + Configureの4つに分かれています。
- ViewControllerからPresenter, UseCaseとProtocolで公開されているメソッドを用いてPresentation層 -> Domain層 -> Data層へと呼び出しを行います。
- Data層ではAPIやLocalのデータを取得しEntityを生成する宣言(※)を返します。
- Domain層ではストリームで流れてきたEntityをPresentation層で利用するModelに変換する等の宣言(※)を返します。
- Presentation層では、Domain層のUseCaseから流れてきたModelを購読・合成・加工を行い、Viewの描画を行います。
※ RxはSubscribeした時にObservableで包まれた中身が処理されるので、Subscribeするまでは、あくまでこんなイベントが流れてきますという定義になります。
各要素の役割
Presentation層
- ViewController / View
- UIやインタラクションを受け持ちます。
- 自発的にUIを描画することはなく、Presenterの通知を受けて描画します。
- Presenter
- UseCaseで記載した宣言を__subscribe__し、合成・加工を行いUIの描画用データを生成します。 (プレゼンテーションロジック)
- Router
- ページ移動を受け持ちます。 (※ 個人の意見ですがコレに関しては必要であればでいいかなと思ってます)
Domain層
- UseCase
- ビジネスロジックを集約します。 (UIを意識する必要がない)
- Data層からObservableのEntityを受け取り、Presentation層用のModelに変換します。
- ここでは基本的にSubscribeはしない。宣言がメイン。
- Translater / Model(ViewModel)
- Presentation層で利用するModelに変換します。
- Model内は、View用のPropertyなども持っておりViewModelの型クラスでもあります。
Data層
- Repository
- Data層にアクセスするためのI/Fです。
- DataStore (Network/Local/Entity)
- Network経由もしくはLocalからデータを取得し、Observableで包んだEntity(値オブジェクト)を返します。
-
ここで生成するEntityがPresentation層で利用されることはありません。
(完全にページからは切り離されたモノ)
Configure
- 外部に位置するもの。依存度注入して疎結合にする。
- (Protocolは今回Router, Presenter, UseCase, Repositoryで用意しましたが、必要だと思う箇所に適宜用意してください)
サンプル
長々と言いましたが、サンプルコードがあった方がいいかなと思い用意しました。
qiita api v2の最新の記事を取得しているだけのシンプルなものですが、こんな感じで色々なページを作っていきます。
(ちなみに認証してないので、1時間60回ぐらい実行するとレスポンスが返ってこなくなります)
実際これぐらいのものであれば、Clean Architectureを用いる必要はないのですが、機能が多くなったりいろんな人が関わってくると効果を発揮します。
ディレクトリ構成
ディレクトリはこんな感じになります。
Domain/Presentasino層はScenes(ページ)ごとに集めています。
.
├── Data
│ ├── QiitaItem
│ │ ├── QiitaItemDataStore.swift
│ │ ├── QiitaItemEntity.swift
│ │ ├── QiitaItemRepository.swift
│ │ └── QiitaItemRequest.swift
│ └── Utility
│ ├── ApiClient.swift
│ └── ArrayTransform.swift
├── Environment
│ ├── Const.swift
│ └── UIStoryboard+Util.swift
├── Scenes
│ ├── List // リストページ
│ │ ├── ListConfigurator.swift
│ │ ├── Domain
│ │ │ ├── ListModel.swift
│ │ │ ├── ListTranslater.swift
│ │ │ └── ListUseCase.swift
│ │ └── Presentation
│ │ ├── Cells
│ │ │ └── ListTableViewCell.swift
│ │ ├── ListPresenter.swift
│ │ ├── ListRouter.swift
│ │ └── ListViewController.swift
│ └── Utility
│ └── Dependencies.swift
└── Storybords
└── List.Storyboard
ディレクトリ構成はこちら
ディレクトリ名 | 役割 |
---|---|
Data | APIやDB、キャッシュ等のデータを操作する処理/Entityがある |
Scenes | 各ページのDomain/Presentationがある |
Storyboards | ストーリーボードがある |
Environment | 全体にかかるもの(ConstやExtensionなど)がある |
テストについて
テストコードについては、Subject通知の確認とデータ件数確認を行う簡単なものを記載しています。
依存度注入できるため、データをいれこむのが手軽で、またRxTestを利用することによってイベントの作成が楽になっています。
実際やってみたメリット・デメリット
-
メリット
-
役割が明確なため、開発するときにこのコードはどこに置こうか悩むことは少なくなります。
-
複数人で開発している時に、UseCaseの〜とかDataStoreの〜とか共通の認識があるので統一感をもたせやすいです。
-
依存度注入もしているので、テストコードが書きやすいです。
-
デメリット
-
導入はコストがあるので、簡単な開発や短期開発であれば向かないかなと思います。
-
上と似たようなものですが、ファイル数は多いので面倒だなと思うことがしばしばあります。
最後に
他の方の記事にもありますが、RxSwiftにしろClean Architectureにしろ手段の一つです。
そのため、必ず選択すれば正解ということはありません。
ただ、選択肢の1つとして知っておくと、より場面に合わせて最適な選択ができるかなと思います。
私も規模が小さいアプリや短期開発でClean Architectureを選ぶかと言われると微妙なラインです。(RxSwiftは慣れると開発スピードが上がるので採用しますが)
参考
Clean Architectureについて
- まだMVC,MVP,MVVMで消耗してるの? iOS Clean Architectureについて
- RxSwiftをプロダクトに導入してみた話
- Clean Swift
- Clean Swift GitHub
- clean-swift-templates
-
Android-CleanArchitecture
(今回は、AndroidにあるMVPよりのCleanArchitectureを参考にしています。)
RxSwiftについて
RxSwiftについてはあまり触れてなかったので、RxSwiftで勉強する上で素敵だった記事を貼ります。
- 今日こそ理解するHot変換
- [マルチスレッドRxSwift @ 社内RxSwift勉強会]
(http://www.slideshare.net/yukitakahashi3139241/rxswift-rxswift-64155110) - [今日こそ理解するHot / Cold @社内RxSwift勉強会] (http://www.slideshare.net/yukitakahashi3139241/hot-cold)
- [RxSwift コードリーディングの勘所@社内]
(http://www.slideshare.net/yukitakahashi3139241/rxswift-rxswift) - [RxSwiftの機能カタログ]
(http://qiita.com/k5n/items/e80ab6bff4bbb170122d) - [RxSwiftの動作を深く理解する]
(http://qiita.com/k5n/items/643cc07e3973dd1fded4) - TestSchedulerとSessionAdapterTypeを使った仮想時間上のテスト