10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【実践編: 内側の2つの円】iOSでVIPERとClean Architecture 円の内側から作る

Last updated at Posted at 2019-10-28

前回の記事の続きのVIPER Clean Architectureの実践編である。

この記事では内側の2つの円の部分まで作成する。今回カバーする部分はとても簡単なのでほとんどの人が問題なく読み進められると思う。

今回作るのは以下の図解の 真ん中の2つの円、EntitiesとUse Cases の部分だ。これを円の内側から順番に作っていく。

CleanArchitecture.jpg

The Clean Architecture by Robert C. Martin (Uncle Bob)

なぜ内側から作るのか?

内側から実装するにのには理由がある。

1. 実装しやすい

円の内側は依存のもっとも深部であり、その先に依存する部分がない。依存する先がないとうことは、依存部分のStubを作ったり、その部分がどうなるかを一切想定しなくても作れるということだ。

2. 作る物の本質がわかる

円の内側は本質的に表現したいことであり、抽象的である。UIの表現に惑わされず本質的に何を作ろうとしているかがわかり、理解を促進する。

3. 実践に近い

実際に開発をする場合、最終的に表現されるUIの部分は、最後まで固まらないことが多い。しかし本質的に何を表現するかは、比較的初期に決まり大きく変化しないことが多い。

例えばユーザーの一覧を表示する画面を作成する時、User Entityを作成し、それをAPI経由でlistとして取ってきて、List状に表示するということは初期に確定するだろう。

しかし実際のUIで具体的にどう表現をするかは最後までわからなかったり途中で変わったりする可能性が高い。

例えばUserをListとして表示する場合、

  • TableView
  • CollectionView
  • PageView(各ページに1ユーザーを表示)

のような複数の実装方法が想定可能である。

もしMVC設計して実際のCell等に直接Userを渡して情報を表示させるような設計をした場合、Clean Architectureで分割されている全てのコンポーネントのコードがViewControllerを中心に密接に繋がってしまい、UI変更のコードへのインパクトはClean Architectureで設計した場合と比べ大分大きくなってしまう可能性がある。

作るシステムの概要

Userの一覧を取ってきて表示するという単純なシステムを作成する。

Simulator Screen Shot - iPhone 8 - 2019-10-28 at 17.39.59.png

FrameworkとフォルダのGroup分け

VIPERやClean Architectureの恩恵を最大限に受けるためには、それぞれのコンポーネントをしっかりFrameworkとして分けることが重要だ。

これにより、依存関係の理解が促進されたり、間違った実装をしてしまうリスクを大きく減らせる。

さらに、1つの画面の開発をUI部分やロジックの部分と複数人で分業しての開発もやりやすくなる。

Group構造とClean Architectureの円を一致させる

Xcode上でRoot配下のGroupは全てClean Architectureの一つの円を表すようにする。

各Frameworkとその配下のディレクトリ構造は、Xcode上と基本的には一致させる。
しかし一部Clean Architectureの円の構造を表現するためにXcode上でGroupはあるが実態としてのフォルダは作成しない部分がある。

具体的にはFrameworkは全てRoot直下に実際のディレクトリを作成する。各Framework配下のディレクトリ構造も基本的にはXcodeのGroupと一致させる。

ただしClean Architectureの外側2つの円は、円の中に複数のFrameworkがあるため、この部分はXcode上ではGroupを作成するが実際のディレクトリは作成しない。さらにこの配下にあるGroupは実際のディレクトリを持つ。

最後にClean Architectureの図解の外側にもう一つ円を追加する。

最終完成形

この記事ではカバーしていない部分も含め全てが完成した場合、このような構造になる。

  • Outer Circle(without folder)
    • App Name(framework)
      • Application(without folder)
      • Routers
    • Settings(framework)
  • View(framework)
  • Interface Adopter(without folder)
    • Presenter(framework)
    • Gateway(framework)
  • Use Case(framework)
    • Request Base
    • Interactors
  • Entity(framework)
    • Entities

今回の記事で作る部分

実際今回の記事でカバーするのは以下の部分のみだ。

  • Use Case(framework)
    • Interactors
  • Entity(framework)
    • Entities

実装準備

もっとも内側のEntityの円の部分を作成し始める前に、事前準備として、Xcodeのプロジェクト作成直後にできたメインのTargetの部分を一番円の外側に作成するもう一つの円の部分を表現するようなXcodeのグループ構成にする。

今回は UserManager という名称のXcodeプロジェクトを作成する。
作成直後、Groupを以下のような構造にする。

  • Outer Circle(without folder: 新たに追加)
    • UserManager(最初から存在するものを移動)
      • Application(without folder: AppDelegateやInfo.plistなど自動生成ファイルを入れておく)

最初の円: Entity

Frameworkとファイルの配置

最初の円であるEntityのFrameworkを作成し、その配下にEntitiesディレクトリを作成。
そこにUser.swiftのファイルを配置する。

  • Outer Circle(without folder: 新たに追加)
    • UserManager(最初から存在するものを移動)
  • Entity
    • Entities
      • User.swift

User Entity

User Entityは以下のようになる。

User.swift
public struct User {

    public let id: Int
    public let name: String

    public init(name: String, id: Int) {
        self.id   = id
        self.name = name
    }
}

何の変哲もない実装だが、重要なのはUser Entityそのものが他のどのFrameworkにも依存していないとうことだ。

実際にUser structが他のどのFrameworkへもアクセスしていないと同時に、EntityのFrameworkのTargetのFrameworks and Librariesの部分で何もImportしないことでそこを確約する。

2つ目の円: Use Cases

Frameworkとファイルの配置

UseCase Frameworkを追加し後述の図解の通りのGroup構造を作成する。

  • Outer Circle(without folder)
    • UserManager
      • Application(without folder)
  • UseCase
    • Interactors(New!)
      • UserListInteractor.swift(New!)
  • Entity
    • Entities
      • User.swift

さらにUseCaseからはEntityへの参照が必須なのでUseCaseのTargetの部分のFrameworks and LibrariesでEntityをインポートする。

Usecase Interactor

ここで追加したUserListInteractorが今回この層で実装するInteractorだ。

UserListInteractor.swift
import Entity

public class UserListInteractor {

    var users: [User] = []
}

UserListInteractorはEntityをimportしてUser Entityを直接参照しており、UserListInteractorはEntityに依存していることがわかる。

さてUserListInteractorはどうやってUserの一覧を取ってくれば良いのだろうか?ここでGatewayが登場する。Gatewayに関しては次回の記事で扱う。

まとめ

今回は非常に単純な例だったが、Entityが分離されているだけでも実際大きな恩恵を受けられる。小さなプロジェクトの場合はEntityだけを分離してそのほかの部分は一つ外側の円に全て入れるようなミニマムなFramework分けでもいいかもしれない。

次回はいよいよVIPER, Clean Architectureの重要かつ比較的難しい部分のより外側の実装に移っていく。

10
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?