Androidのパッケージ構成について考えてみた

# 更新しました (2017/4/12)

この記事を書いたときから大分時間が経ったので、最新版を[こちら](http://kobakei.hatenadiary.jp/entry/2017/04/10/173226)に書き直しました。

# 基本的な階層構造

AndroidでMVCを適用しようとすると、Activity/FragmentがViewとController両方の責務を担っていて綺麗にVとCを分離できず、もやっとしてしました。そこで、色々な資料やサンプルを見たところ、 http://www.slideshare.net/mokemokechicken/iosandroidmodel にある階層構造が一番しっくり来たのでこれをベースにパッケージ構造を考えることにします。

ユーザー操作に近い順に、以下の３階層に分類されます。

- プレゼンター層
    - UIの制御やユーザーの入力に対する制御を扱う層。MVCでいうとVCにあたる。
- ビジネス層
    - ビジネスロジックを扱う層。MVCでいうとMのうち抽象度の高い部分。
- データ層
    - データリソース（データベースやサーバーAPIなど）へのアクセスを行う層。MVCでいうとMのうち抽象度の低い部分。

# パッケージ構造

基本的な階層構造をもとに、もう少し細かい単位で分けたものをパッケージ構造に一致させます。

```
com.example.myapp
|
|  (プレゼンター層)
|- activities
|- fragments
|- views
|
|  (ビジネス層)
|- usecases
|- entities
|
|  (データ層)
|- network
|- storage
```

または、階層を強く意識させるなら、一段階掘ってこういう階層構造もありかと（やりすぎ？）

```
com.example.myapp
|
|- presenters
|  |- activities
|  |- fragments
|  |- views
|
|- business
|  |- usecases
|  |- entities
|
|- data
   |- network
   |- storage
```

# 各パッケージ

## activities

Activityが入るパッケージです。Activityが担うべき責務としては、

- Fragmentの追加・変更・削除
- ActionBar, Menu, Drawerの管理
- 他のActivityへの遷移

などがあります。ActionBar以外の画面内のビューについては、Fragmentにやらせるので触らないようにします。

## fragments

Fragmentが入るパッケージです。Fragmentが担うべき責務としては、

- UIの制御（ActionBarは除く）
- ユーザーの操作やライフサイクルに合わせて、Model(後述するusecases)を呼び出す。また、処理完了時の通知を受け取ってUIに反映する

があります。

なお、Fragment in Fragmentはライフサイクルがややこしくなるので避けるのがベターだと思っています（Fragment in ViewPager in Fragmentとかは除く）。複数のFragmentで共通して利用したい部品は、後述するviewsパッケージにカスタムビューを作ります。

## views

カスタムビューが入るパッケージです。色々なFragmentで共通して利用するViewやView周りのロジックを共通化します。

## usecases

ビュー(Activity, Fragment, etc)が直接呼ぶビジネスロジックをここに入れます。Twitterクライアントアプリを例にすると、

- ログイン済みかどうかをチェックする
- ツイートする
- 自分のタイムラインを取得する

などが挙げられます。

内部では後述するnetwork/storageパッケージのクラスを使ってデータの取得や更新を行います。自分のタイムラインをRESTful APIを叩いてサーバーから取得するかSQLiteのキャッシュを使うかなどはビューが知るところではないので、usecasesクラスのAPIでは抽象化します。

```java:TwitterUseCase.java
/**
 * Twitter関連のビジネスロジック
 */
public class TwitterUseCase {

    /**
     * タイムライン取得時にプレゼンテーション層に返すイベント
     */
    public class TimeLineEvent {
        public final List<Tweet> tweets;
        public TimeLineEvent(List<Tweet> tweets) { this.tweets = tweets; }
    }

    /**
     * タイムラインを取得する
     * これを呼ぶクラス（Fragment）は、中でどんなデータリソースにアクセスしてるかは知らない。
     */
    public void getTimeline() {
        // ローカルキャッシュから取得
        List<Tweet> tweets = LocalTweetCache.getAll();
        if (tweet != null) {
            EventBus.getDefault().post(new TimeLineEvent(tweets));
            return;
        }

        // キャッシュから取れなかった場合、APIから取得
        ApiClient apiClient = new ApiClient();
        apiClient.getTimeLine(new TimeLineListener() {
            @Override
            public void onSuccess(List<Tweet> tweets) {
                EventBus.getDefault().post(new TimeLineEvent(tweets));
            }
        });
    }
}
```

http://www.slideshare.net/mokemokechicken/iosandroidmodel にある通り、非同期処理を行った後のビューへの結果の返し方は、通知を使います。ライブラリには [EventBus](https://github.com/greenrobot/EventBus) を使います。ビューのライフサイクルは短いのですが（ユーザーの操作によって簡単に破棄されるので）、EventBusでは通知先が死んでいても気にせずにイベントをpostできて楽です。

あとusecasesという名前はいまいちしっくり来てないので、他にいい名前がないか考えています。modelsだとカバーする範囲が広すぎるし、managersもなあ。。。

## entities

データそのものを表すPOJOが入るパッケージです。データの取得や保存は後述するstorageやnetパッケージのクラスにやらせるので、純粋なデータを表すだけのクラスにします。

データ層でデータベースやRESTful APIから取得した生のデータからEntityに変換され、そのままビジネス層, プレゼンテーション層へと渡っていきます。

```java:Tweet.java
public class Tweet {
    public long id;
    public User user;
    public String body;
    public Date createdAt;
}
```

## network

RESTful APIのクライアントなど、ネットワーク関連のクラスが入るパッケージです。APIクライアントのやるべきことを時系列に並べると、

- 設定やパラメータからHTTPリクエストを組み立てる
- HTTPリクエストを発行する
- HTTPレスポンス（主にJSON文字列）をパースし、Entityに変換する
- 呼び出し元のスレッドで、Entityを渡したコールバックを実行する

という流れになります。自分は内部実装には [Volley](https://android.googlesource.com/platform/frameworks/volley) + [Gson](https://code.google.com/p/google-gson/) をよく使います。

## storage

データベース（SQLite, SharedPreferences, ローカルファイル etc）関連のクラスが入るパッケージです。

- データベースからEntityを取得
- Entityをデータベースに保存

などを行います。

内部では [ActiveAndroid](http://www.activeandroid.com/) などのORMapperやContentProvierを使用してデータベースへのアクセスを行います。


# 最後に

こんな感じでパッケージを分けておくと、クラスを設計するときにパッケージの粒度に従って設計すればよいので、複数メンバーで作業していても抽象化のレベルを統一でき、楽にクラスの責務を分離できるのではないかと思っています。

開発が進むにつれてアプリが大きくなっていくと他のアーキテクチャや設計に変えるかもしれませんが、今のところはこれくらいがちょうどいいような気がしています。

# 参考資料

- http://suzukaze.hatenablog.jp/entry/2015/02/22/221433
    - 定番のベストプラクティス。
- http://www.slideshare.net/mokemokechicken/iosandroidmodel
    - 大変参考になった資料。モデルの設計とか。
- http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/
    - これもとてもいい資料。階層分けなど大体同じです。サンプルは https://github.com/android10/Android-CleanArchitecture
- https://github.com/antoniolg/androidmvp
    - MVPアーキテクチャの例。Viewの抽象化をするとこが冗長な気がしてやめた。