OrmaとDagger2でRepositoryレイヤーを作成する - Android

## システムをレイヤー化し、各レイヤーに独自の責任を持たせよう

データをローカルに格納するためにSQLiteに保存する場合がありますが、データベースの作成とかアクセスとかCursorとか少し面倒です。😧

自分はそんなめんどくささから、[Orma](https://github.com/maskarade/Android-Orma)と[Dagger2](https://github.com/google/dagger)を使用しています。
OrmaはAndroid用のORMで、実装としてはSQLiteDatabaseのラッパーとなっております。

- [Android Orma入門](http://gfx.hatenablog.com/entry/2016/01/25/215003)

Dagger2はGooglの依存性注入ライブラリーです。

- [Dagger2 入門解説](https://qiita.com/MoyuruAizawa/items/26cb093adbc778013467)


Repositoryレイヤーはこんな感じにしています。

![Repository behind the scenes (1).png](https://qiita-image-store.s3.amazonaws.com/0/206165/ff924a82-8894-b4be-b6ed-b5188ce4ab4b.png)

MVPなどで実装する場合、ご覧のようにPresenterはRepositoryと依存関係を持つ感じで、Repositoryオブジェクトだけでデータベース関連の操作を実行します。そうすることでアプリ全体のデータベースエントリーポイントとなります。

では深掘りして動作を理解していきましょう。

[サンプルアプリ](https://github.com/wataanaber/viper-todo)を作成しましたのでご覧ください。
シンプルなTodoアプリです。

まずデータベーステーブルのアクセスに関するOrmaとmodel周りから
`Ormaでは、テーブルに対応するスキーマはPOJOにアノテーションを付加したものです。Ormaのannotation processorがアノテーションをみてヘルパークラスを生成します。`(引用:[モデルクラスの定義](http://gfx.hatenablog.com/entry/2016/01/25/215003#モデルクラスの定義)

```
@Table
public class Todo {

    @PrimaryKey(autoincrement = true)
    public long id;

    @Column(indexed = true)
    public String title;

    @Column
    @Nullable // allows NULL (default: NOT NULL)
    public String content;

    @Column(indexed = true)
    public boolean done;

    @Override
    public String toString() {
        return "Todo{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", done=" + done +
                '}';
    }
}
```

次にデータベースを実行する上での基本的なクエリをインターフェースに定義します。

```
public interface DataSource<T> {
    void add(T item);
    void add(List<T> items);
    void update(T item);
    void update(List<T> items);
    void remove(T item);
    boolean isEmpty();
}
```

SELECT系のクエリはモデルによってフィルターをかけたり、Rxで処理したかったりと別れる場合があるのでDataSource<T>を継承したインターフェースを定義しています。

```
public interface TodoDataSource extends DataSource<Todo> {
    Observable<Todo> fetchAsObservable(long id);
    Single<List<Todo>> fetchAllAsSingle();
}
```

データベースハンドルであるOrmaDatabaseを使用してCRUD操作をする必要があるため、Daggerを使用して依存関係を作ります。

```
public class LocalTodoDataSource implements TodoDataSource {

    private OrmaDatabase ormaDatabase;

    @Inject
    public LocalTodoDataSource(OrmaDatabase ormaDatabase) {
        this.ormaDatabase = ormaDatabase;
    }

}
```

これでデータベースのクエリを実行できる用意ができたので、Presenterがデータソースとやり取りできるようにするエントリーポイントであるRepositoryのインターフェースを作成します。

```
public interface Repository {
    LocalTodoDataSource localTodoData();
}
```

そしてアクセスするためのRepositoryImplを作成します。

```
public class RepositoryImpl implements Repository{

    public LocalTodoDataSource localTodoDataSource;

    @Inject
    public RepositoryImpl(LocalTodoDataSource localTodoDataSource) {
        this.localTodoDataSource = localTodoDataSource;
    }

    @Override
    public LocalTodoDataSource localTodoData() {
        return localTodoDataSource;
    }
}
```

最後に、Presenterにすべてのデータソースに依存するリポジトリレイヤを準備しますが、これらの依存関係はDaggerによってつくられます。 Daggerはこれらのデータソースの単一のインスタンスを保持し、必要なときにいつでも提供(Provide)します。

これで、リポジトリの依存関係を提供し、Todoのローカルデータを取り出すことができるようになります。

```
public class TodoListPresenter implements TodoListContract.Presenter {

    Repository repository;
    
    @Inject
    public TodoListPresenter(Repository repository) {
        this.repository = repository;
    }
}
```

以上です。いかがでしょうか。リポジトリレイヤーを作成し、インターフェースでやりとりすることで保存する仕組みをOrma以外にRealmやPref、リモートのデータソースと切り離すこともできるようになると思います。

ご覧いただきありがとうございました！ 😃

Happy Coding!