まだMVC,MVP,MVVMで消耗してるの？ iOS Clean Architectureについて

＜この記事は「[Money Forward Advent Calendar 2015](http://qiita.com/advent-calendar/2015/moneyforward)」の22日目の記事です＞

この記事は、iOS Clean Architectureと実際にコードへ適用した内容について紹介します。
コードについては、改善の余地があるため随時修正していくと思います。
→ github: https://github.com/koutalou/iOS-CleanArchitecture

## iOS開発においてよくある問題点

* 「ビジネスロジックはModelに置くべき」と言うが、開発者によって理解や意見がバラバラで統一的な実装ができない
* 度重なる仕様変更や複雑な仕様に対応するためにViewControllerや特定のModelが肥大化し、ビジネスロジックの本質を見失う
* MVC,MVP,MVVMだけで考えると、どこかのレイヤが複数の責務を持つことになり依存度の高い複雑なコードが生まれてしまう

#### → 上記を解決すべくiOS Clean Architectureについて紹介します

# Clean Architectureについて

Clean Architectureとは、一言で言うとドメイン駆動開発（DDD）やユースケース駆動開発（UCDD）を意識して、ビジネスロジックをUIやFrameworkから引き離し、それぞれの層毎に役割と責任を分離したArchitectureになります
 http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

![CleanArchitecture.jpg](https://qiita-image-store.s3.amazonaws.com/0/19097/903dd9be-6941-3a04-2e31-1a80429496c2.jpeg)

### Clean Architectureを使うことのメリット

* ビジネスロジックが明確になる
    *  特定のClassにロジックが盛り込まれFatになり、他の役割との依存性が高くなる事態を防ぐ
* フレームワークに依存しない
    * Clean Architectureは特定のフレームワークやライブラリに依存しないです
* UIに依存しない
    * ビジネスロジックを変えずにUIのみ変更することができる
* Data Storeに依存しない
    *  データ連携先がサーバでも、端末内のDBでもビジネスロジックに影響が無い
    * ビジネスロジックは、データの保存先や取得先を知らないくて良い
* テストの容易性
    * 全ての層でテストが導入しやすい

### Clean Architectureを使うことのデメリット

* コード量が多い
    * 役割毎に層が多くなるため、必然的にコード量が多くなります
    * プロトタイプや短期開発アプリケーションには向かないと思います

## 他のArchitectureとの比較

### MVC architecture
<img width="940" alt="mvc.png" src="https://qiita-image-store.s3.amazonaws.com/0/19097/21595778-f88d-b066-8540-e7c64c50e816.png">

### MVP(MVVM) architecture
<img width="940" alt="mvp_mvvm.png" src="https://qiita-image-store.s3.amazonaws.com/0/19097/5e3140f2-a018-8cb4-aa00-83b1d82133d4.png">

### DDD architecture
<img width="950" alt="ddd.png" src="https://qiita-image-store.s3.amazonaws.com/0/19097/a89eaf6b-98e2-8f18-3aff-0f5b36697387.png">

### iOS Clean Architecture

* 構成としては、DDD(ドメイン駆動開発)のDomain層, Data層をもう少し細かくレイヤを分けたようなイメージです

<img width="940" alt="clean_architecture.png" src="https://qiita-image-store.s3.amazonaws.com/0/19097/e70a4605-e066-b0c2-5c6e-678c71d595ce.png">

# 各レイヤの役割と説明

## Presentation layer
* UIの表示やイベントのハンドリングを行います、ビジネスロジック処理はしません

### View (ViewController)
 * 画面表示やユーザのタッチイベントなどのイベントをPresenterに通知する
 * Presenterから受けたModelのデータやステータスによりViewの表示を切り替える

### Presenter
 * Viewからイベントを受け取り、必要があればイベントに応じたUseCaseを実行する
 * UseCaseから受け取ったデータをViewへ渡す
 * Viewがどうなっているか知らない

## Domain layer

* iOSアプリケーション依存のビジネスロジックはこのレイヤが担当します
 
### UseCase
* ユースケースに必要なロジック処理を記述する
    * どのデータをどのように取得するかここで実装する
* UIには直接関与しない(View,ViewControllerから直接参照されない)

### Translater
* UseCaseで取得したEntityをPresentation層で使用するModelへ変換する
    * Viewで使用するために最適化したModelの作成を行う

### Repository
* UseCaseで取得したいデータのCRUD相当のI/Fを記述する
    * データ取得に必要なDataStoreへデータ処理のリクエストを行う
* Repositoryは、データを扱うI/Fを定義するがどうやってデータを扱うか知らない
* Domain層として記述しましたが、実態としてはDomain層とData層のI/Fです

## Data layer

* 通信やデータ管理のロジックはこのレイヤが担当します

### DataStore
* データを実際に取得更新する処理を記述する
* サーバからデータを取得するか、DBやキャッシュのデータを使用するかどうかもここで判断する
    * iOSでは、API通信、Realm、CoreData等を扱う実装に相当
* 複数のDataStoreを扱う場合はFactoryパターンを用いてRepositoryがData種別を意識しない設計にする

### Entity

* DataStoreで扱うことができるデータの静的なモデル
* Entity自身を直接操作することはせず、Value objectとして使用する
* EntityはPresentation層では使用されない


# Sampleアプリケーション

* 上記で一通り説明はしましたが、実際にコードを見た方が理解は早いと思うので、サンプルを作成しました
 * Twitterアカウントと連動してTimelineを表示するサンプルアプリケーションです

→ github: https://github.com/koutalou/iOS-CleanArchitecture

## サンプルアプリケーションの特徴

### 低依存性
* Presenterから見たViewとUseCaseのI/F
 * 各レイヤが依存しないようにViewとUseCaseでは以下のようにprotocolでI/Fを定義しており、Presenterが最低限必要なI/F以外参照しなくて良い構成となっています

```swift:ICATTimelineViewController.swift
protocol ICATTimelineViewInput: class {
    func setTimelinesModel(_: ICATTimelinesModel) -> Void
    func changedStatus(_: ICATTimelineStatus) -> Void
}
```
```swift:ICATTimelineUseCase.swift
protocol ICATTimelineUseCaseOutput: class {
    func loadTimelines(timelinesModel: ICATTimelinesModel)
    func notAuthorizedOrNoAccount()
    func loadTimelinesError(error: ICATError)
}
```

### ビジネスロジックの明分化
* このiOS Clean Architectureでは以下のようにビジネスロジックを明分化します
    * UseCase: アプリケーションに依存する処理のビジネスロジック
    * Translater: Viewに最適なModelの変換ロジック
    * DataStore: 通信したり永続化したデータを扱うロジック
* これにより、上記3つのようなロジックが混合してロジックが複雑になるようなケースを防ぐことができます

### RepositoryのTask化
* RepositoryをSwiftTaskのTaskでラップすることで、UseCaseのビジネスロジックを容易に実装できるようにしています
* TDDの観点からもテストを容易にするために良さそうです
* RepositoryをSwiftTaskでラップすると決めた場合、他の全てのRepositoryもSwiftTaskでTask化する必要があります

RepositoryのSwiftTask I/F実装 

```swift:ICATSocialAccountRepository.swift
lazy var dataStore: ICATSocialAccountDataStore = ICATSocialAccountDataStore()
func getTwitterAccountsTask() -> Task<Void, Array<ACAccount>?, ICATError> {
    return Task { _,fulfill, reject, configure in
        self.dataStore.getTwitterAccounts({ (accounts, error) -> Void in
            if (error.isError) {
                reject(error)
                return
            }
            
            fulfill(accounts)
        })
    }
}
```

UseCaseのTask処理実装

```swift:ICATLoginAccountUseCase.swift
weak var output: ICATLoginAccountUseCaseOutput?
lazy var loginAccountRepository: ICATLoginAccountRepository = ICATLoginAccountRepository()
lazy var socialAccountRepository: ICATSocialAccountRepository = ICATSocialAccountRepository()
var twitterAccountIdentifier: String?        

// このケースではSwiftTaskで同期処理
loginAccountRepository.getSelectedTwitterAccountTask().success { (identifier) -> Task<Void, Array<ACAccount>?, ICATError> in
    twitterAccountIdentifier = identifier
    return self.socialAccountRepository.getTwitterAccountsTask()
}
.success { (accounts) -> Void in
    self.acAccounts = accounts!
    let registeredAccountsModel = ICATRegisteredAccountTranslater.generateRegisteredAccount(accounts!, selectedIdentifier: twitterAccountIdentifier)
    self.output?.loadTwitterAccounts(registeredAccountsModel)
}
```

## 実装内容の説明
* サンプルだけ見ても分からないかもしれないので、Timelineを表示する画面に絞って説明します

## View
###  ICATTimelineViewController.swift
#### 役割
* データがあればTimelineを表示する
* ステータスを教えてもらえれば適切な表示を行う

#### 実装
 * Presenterへ画面表示のタイミング(イベント)を通知する
     * この画面ではタッチイベントなど他のイベントはなし
 * PresenterからViewへ通知してほしい情報をprotocolで実装する 
 * PresenterからModelやステータスを受け取り表示を変える

#### 特徴
* 実際のコードを見るとわかると思いますが、最低限のイベント処理とModelやステータスに合わせたViewの更新しか実装がないため、ViewやViewControllerはかなりシンプルになります

## Presenter
### ICATTimelinePresenter.swift
#### 役割
* ViewとUseCaseのイベントとデータの橋渡しをする

#### 実装
 * Viewから表示イベントを受け取り、Timelineデータ取得のUseCase(ICATTimelineUseCase)を実行する
 * UseCaseから受け取ったデータをTranslaterへ渡して整形する
     * データ整形(Entity -> Model)後にViewへ渡す
     * データの状態やエラーによりViewにステータス情報を渡す

#### 特徴
* Entity, Model変換はUseCase/Translaterが担当するためデータの修正は必要なく、イベントやデータの受け渡しとステータスの管理しかしないため、実装内容はかなりシンプルになります

## UseCase
### ICATTimelineUseCase.swift
#### 役割
* タイムラインデータ取得に必要な処理(実際に行うのは下位レイヤ)を行う
    * 同期的に行うのか非同期で行うのか、要求に合わせて実装する

#### 実装
* Twitterアカウントを取得して、Timelineデータを取得する処理を行う

#### 特徴
* 今回RepositoryのI/Fを全てSwiftTaskで実装してあり、UseCaseではTaskとしてデータを扱う処理のロジックを記述することができる
* TDDも意識しており、Repositoryのテストを行うことも容易にできるようになります

## Translater
### ICATTimelineTranslater.swift
#### 役割
* EntityをViewの表示に最適なModelへ変換する

#### 実装
* Entity -> Model変換

## Repository
### ICATTimelineRepository.swift
#### 役割
* UseCaseから依頼されたTimelineデータ取得を行う

#### 実装
* DataStoreへデータ取得をリクエストする
* UseCaseからの制御をTask化できるようにI/FをSwiftTaskでラップ

#### 特徴
* I/FをSwiftTaskでラップしているため、UseCaseのビジネスロジックが複雑でも容易に実装できる

## DataStore
### ICATTimelineDataStore.swift
#### 役割
* サーバまたはRealmからデータを取得してRepositoryに返す

#### 実装
* Realmにデータがあるか確認しあればデータをRepositoryに渡す
* サーバにリクエストして、帰ってきたデータをRepositoryに渡してRealmへ更新する

#### 特徴
実際にデータを取得したり更新したりする処理を一元管理できる

## その他

### DIについて

Clean ArchitectureやDDDにはDI(Dependency Injection)がありますが今回はあまり触れていません
本来のClean ArchitectureはDIを前提としており、よりメリットを享受するためには更に依存性を少なくする必要があります
コード的には、1つ上位のレイヤが1つ下のレイヤに依存することを決めていますが、Routing classを新たに設けて、Routingがそれぞれの依存関係を繋ぎ合わせるような実装にして各レイヤ間を疎結合にするイメージです

ただ、DIを考慮していない現時点でも既に導入障壁が高いため今回はここまでにしたいと思います。

### 敷居が高い

上記で少し述べましたが、導入時の敷居が高く慣れるまで大変だと思います
簡単なアプリケーションや継続的に開発しないアプリケーションでは必要ないかもしれないです

### テストの容易性

何度か述べていますが、役割によりレイヤを明確に分けているためレイヤ毎のテストが可能です
長期的な開発・想定していない仕様に対応していくサービスほど、テストがより重要になってくるかと思います

### Modelに対する理解
ここまで読んでいれば、「ビジネスロジックはModelに置く」ということがどれだけ曖昧な言葉かわかると思います
Modelと言っても、Clean ArchitectureではDomain/Data層に分かれ、更にUseCase/Translater/Repository/DataStore/Entityに分かれます

世間一般の「Model」は「Model層」と呼ぶ方が正しく、今後の開発でModelに対する理解がもっと広まれば良いなと思います、ビジネスロジックも同様ですね

## まとめ

ここまでiOS Clean Architectureについて説明しましたが、必ずしもClean Architectureを導入することが正ではなく、考え方やメリット/デメリットを理解した上で状況に合わせてより良い開発手法で開発していくことが重要だと考えています

この記事がその手助けとなれば嬉しいです

## 参考文献

The Clean Architecture
[The Clean Architecture | 8th Light](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html)

Android Clean Architecture
[AndroidオールスターズでClean Architectureについて発表してきた&参考リンク集 - tomoima525's blog](http://tomoima525.hatenablog.com/entry/2015/08/13/190731)

MVC, MVP, MVVM関連
[Webアプリケーション開発者から見た、MVCとMVP、そしてMVVMの違い - Qiita](http://qiita.com/shinkuFencer/items/f2651073fb71416b6cd7)
[MVVMのModelにまつわる誤解 - the sea of fertility](http://ugaya40.hateblo.jp/entry/model-mistake)
[MVVMをベースに複雑な振る舞いをしっかり把握できるアプリ開発 - Qiita](http://qiita.com/susieyy/items/2af5321b287b8d2f49f6)

VIPER, DDD, UCDD関連
[Meet VIPER: Mutual Mobile's application of Clean Architecture for iOS apps - Mutual Mobile](https://mutualmobile.com/posts/meet-viper-fast-agile-non-lethal-ios-architecture-framework)
[AndroidではMVCよりMVPの方がいいかもしれない - Konifar's WIP](http://konifar.hatenablog.com/entry/2015/04/17/010606)