ProviderとかViewModelとかRepositoryとかDIとかMVVMとかよく分かんねぇよ！って人のために

# はじめに
こんにちは！グロービスで「ナノ単科」の開発を担当しています、GDPの堀尾です。
[GLOBIS Advent Calendar 2023](https://qiita.com/advent-calendar/2023/globis) の17日目として、普段開発で使用しているFlutterについて書いてみたいと思います。

## ナノ単科とは？
https://mba.globis.ac.jp/nano-mba/

ナノ単科はグロービス経営大学院が2021年10月から提供している教育プログラムです。動画学習はもちろん、AIを使った学習コンテンツや大学院講師によるライブ授業、受講生同士で学習理解を深めるグループワークなどを通じて、6週間で仕事に活かせる基礎スキルを身につけることができます。

# 本題
Flutterを勉強してたらよく見るこういうコード。
特にRiverpod関連で。。。

```
final hogehogeViewmodelProvider = StateNotifierProvider.autoDispose<HogehogeViewmodel, hogehoge>(
  (ref) => HogehogeViewmodel()),
);

class HogehogeViewmodel extends StateNotifier<AsyncValue<Hoge?>> {
  hogehogeViewmodel() : super(const AsyncValue.loading())
}
```

**ViewModelって何？ViewModelProviderって何？**

```
abstract class HogehogeRepository {
  ...
}
```

**なんでabstract classで定義してるの？**

**ってかそもそもRepositoryって何？**

```
class HogehogeRepositoryImpl extends HogehogeRepository {
  HogehogeRepositoryImpl();

  @override
  Future<Hoge> doSomething() async {
    ...
    }
}
```

**実装はImplでって何？**

```
final hogehogeRepositoryProvider = Provider.autoDispose<HogehogeRepository>(
  (ref) => HogehogeRepositoryImpl(),
);

```

**RepositoryProviderって何？**

# ViewModelとかProviderとかRepositoryとかMVVMとかよくわかんねぇよ！

その気持ちわかります。

# 上記のコードはMVVMパターンとRepositoryパターンを組み合わせて作ってる & RiverpodをつかってDIして作ってる

**は？**

以下解説

# MVVMって何？

![mvvm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211122/451caba9-2e15-cd17-4ed4-6bb359c100b6.png)

ざっくりいうと、

- Model : データの加工、取得、保存などが役割
- View : ViewModelの情報を使用してUIに表示、ViewModelにアクションを送信
- ViewModel : ModelからのUIに描画するための情報を変換・保持する

ViewModelは変更があったデータを即時Viewに反映してくれる

```
lib
 ┣ models
 ┣ viewmodels
 ┣ views
 ┣ app.dart
 ...
```

普段開発しているナノ単科のディレクトリ階層にも、modelとviewmodelとviewを用意しています。
（※ 現在ナノ単科ではviewmodelという用語は使用していません、notifierにすべて置き換えました。この記事ではMVVMモデルで馴染みの深いviewmodelを使用して解説していきます）

## MVVMのメリット

- Viewに素早くデータを反映することができる
- 画面更新の処理をわざわざ書かなくてもいいので、その分他のことに気を使える
    - ↑ データバインディングという仕組みのおかげ、気になる人は調べてみて
    

# Repositoryパターンって何？

![mvvm_r.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211122/aaddc814-b9a3-d8e7-08c8-f9a9b74fffaf.png)


簡単に言うと、データソースからデータを引っ張ってくる処理は抽象的なレイヤ（Repository）に任せようね、というつくり。

これの何が嬉しいかというと、基本的に**データを扱う側はそのデータがどこから来たかなんてどうでもいい**ので、**それだけ疎結合になるので依存性が下がる**わけです。

「データくれ」といってデータをくれればそれで良いわけです。

なのでRepositoryが色んなところからデータを持ってきてくれるけど、ViewModelなんかは出処を知らなくても良い → **疎結合になって嬉しい**

実際の現場ではデータソースはバックエンドだったり、外部のAPIだったりするでしょう。
ナノ単科でも色んなところからデータを引っ張ってきています

```
models
 └ repositories
    ┣ database
    ┣ graphql
    ┣ sharedpreference
    └ remote
```

- database
    - ブラウザのデータベースからデータを取得する
    - ※ ちなHiveというパッケージでブラウザDBを扱える
- graphql
    - バックエンドのDBからデータを取得する
- sharedpreference
    - SharedPreferenceというストア的なものからデータを取得する
- remote
    - いろいろ、上記以外の外部APIとか

## MVVM + Repositoryパターン

![mvvm_r.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211122/8ba21b04-80bc-1bdc-f56b-4cee32a94587.png)

実はさっき出てきた画像（↑）はMVVMとRepositoryパターンを組み合わせた画像でした。

Flutterでよくあるパターンとして上記のように作っているプロダクトも多いのではないでしょうか。

# DIって何？

DIとはDependency Injectionの略で、日本語にすると**依存性の注入**です。

**依存性の注入**っても何がなんだか分かんねぇよ、という感じですね。

簡単に言うと、

> 「[dependency](http://d.hatena.ne.jp/keyword/dependency)」は使われるオブジェクト（サービスと呼ぶ）であり、「injection」とはそのオブジェクト（＝サービス）を、それを使うオブジェクト（クライアントと呼ぶ）に渡すことです。
> 

です。あるクラスで使うオブジェクトを外部から渡してやろうということです。

## DIじゃない例

あるサービスクラスがあるとします。

```dart
class Service{
  void doSomething(){
    print('hogehoge');
  }
}
```

このとき、クライアントクラスでサービスクラスを扱うときに、以下のような書き方をすることがあると思います。

```dart
class Client{
  Service _service;

  Client(){
    this._service = new Service();
  }

  void doSomething(){
    _service.doSomething();
  }
}
```

この場合、ClientクラスのコンストラクタでServiceクラスを呼び出しているので、ClientクラスはServiceクラスに依存していると言えます（密結合）

## DIしてみる

ちょっとDIしてみましょう。

先程のClientクラスを以下のように書き換えます。

```dart
class Client{
  Service _service;

  Client(this._service);

  void doSomething(){
    _service.doSomething();
  }
}
```

これだと、外からServiceクラスを渡してくれているので、依存性がちょっと下がりました。

ちなみにClientクラスを使う時は以下のような感じになります。

```dart
void main() {
  final service = Service();
  final client = Client(service);  // ServiceをClientに渡す（注入）
  client.doSomething();  // hogehoge
}
```

外からServiceクラスを渡していますね。

## もうちょいDIしてみる

先の例では外からオブジェクトを渡していましたが、これだと**Serviceクラスしか渡せない**という問題があります。Service2とかが渡せないわけです。

もうちょい依存性を下げてみましょう。

まずは、Serviceクラス用のインターフェースを用意します。

```dart
abstract class ServiceInterface {
  void doSomething();
}
```

このインターフェースをつかってServiceクラスを実装してみます。

```dart
class Service extends ServiceInterface {
  void doSomething() {
    print('hogehoge');
  }
}
```

プラスでService2も作ってみましょう

```dart
class Service2 extends ServiceInterface {
  void doSomething() {
    print('hogehoge2');
  }
}
```

Clientクラスをちょっと書き換えます

```dart
class Client {
  ServiceInterface _service; // ← ServiceInterfaceを受け取るようにする

  Client(this._service);

  void doSomething() {
    _service.doSomething();
  }
}
```

すると

```dart
void main() {
  Client(Service()).doSomething();   // hogehoge
  Client(Service2()).doSomething();  // hogehoge2
}
```

ServiceクラスもService2クラスも注入できるようになりました。

## 何が嬉しいの？

なんでこんな作り方をするか？ですが、

ここで、上記のServiceクラスが外部からデータを取得するクラスだとイメージしてみてください。

```dart
class Service extends ServiceInterface {
  void doSomething() {
    // HTTP通信とかで外部からデータを取得する
  }
}
```

さらに、「Clientのテストをしたい」場合、この作りが便利になってきます。

例えば、Service2クラスをモックにすると、

```dart
class Service2 extends ServiceInterface {
  void doSomething() {
    // Jsonとかからテストデータを取得する
  }
}
```

ClientクラスにService2クラスを渡すだけで、外部と通信をしなくてもテストデータでClientクラスのテストができるわけです。

Clientクラスは正常なデータが渡ってきさえすればロジックのテストができるので、データがどこから渡ってくるか知ったこっちゃないわけです。

この感じ、さっきもRepositoryパターンのところで見ましたね。

## **なんで**Repositoryは**abstract classで定義してるの？**

さて本題です。

```
abstract class HogehogeRepository {
  ...
}
```

```
class HogehogeRepositoryImpl extends HogehogeRepository {
  HogehogeRepositoryImpl();

  @override
  Future<Hoge> doSomething() async {
    ...
    }
}
```

Repositoryがなぜabstract classで定義されているかというと、DIを行うためというわけでした。

さっきの例で言う`ServiceInterface` が上記のコードで言う`HogehogeRepository` 、Serviceクラスが`HogehogeRepositoryImpl` に対応しています。

テストをしたい場合は、`HogehogeRepositoryImpl` をモックにしたりすることで、わざわざ外部通信（GraphQLとか使ってバックエンドのDBから値を取得）することなく、テストデータでテストできるという利点があります。

# 〇〇**Providerって何？**

もうちょっとDIの話をしてみます。

```
final hogehogeViewmodelProvider = StateNotifierProvider.autoDispose<HogehogeViewmodel, hogehoge>(
  (ref) => HogehogeViewmodel()),
);

class HogehogeViewmodel extends StateNotifier<AsyncValue<Hoge?>> {
  hogehogeViewmodel() : super(const AsyncValue.loading())
}
```

```
final hogehogeRepositoryProvider = Provider.autoDispose<HogehogeRepository>(
  (ref) => HogehogeRepositoryImpl(),
);

```

Flutterのコードを見てると、〇〇Providerってのがよく出てくると思います。

〇〇ViewModelProviderとか〇〇RepositoryProviderとか、後ろにProviderってのがついたオブジェクトがよく出てきます。

## Providerとは何か？

Providerとは、Riverpodで使用される状態を監視するためのオブジェクト的なものです。監視と言いましたが、簡単に言うと「値が変更されたらお知らせしてくれる」みたいな感じです。

上記のような書き方は、Riverpodのお決まり的な書き方になります。

Riverpodはアプリの状態管理に使われるパッケージで、状態（値）が変わると自動的に通知し、画面を再描画してくれるというものです。

Providerを使うことで、アプリの様々な場所（ほぼどこからでも）から状態（値）にアクセスできるようになります。

こんな感じで

```dart
ref.watch(hogehogeViewModelProvider)
```

アプリの様々な場所（ほぼどこからでも）というのがミソで、Riverpodの前身となるProviderというパッケージ（名前がややこしいですが、この章で解説しているProviderとは別物です。パッケージ名です）では、アクセスできる場所が限られていたのですが、Riverpodではどこからでもアクセスできるようになりました。

## Providerに実装を注入

よくあるパターンとして、`hogehogeRepositoryProvider` に、`hogehogeRepositoryImpl` が注入していたりします。

```dart
final hogehogeRepositoryProvider = Provider.autoDispose<HogehogeRepository>(
  (ref) => hogehogeRepositoryImpl(),
);
```

これもいわゆるDIになっています。

`hogehogeRepositoryProvider` に対して、`hogehogeRepositoryImpl` という実装クラスを注入してくれているわけです。

これによって、`RepositoryImpl` をモックなどに差し替えたりするとテストがしやすいというメリットが生まれます。

# 参考

https://wasabeef.medium.com/flutter-%E3%82%92-mvvm-%E3%81%A7%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B-861c5dbcc565

https://kabochapo.hateblo.jp/entry/2019/07/01/211953

https://themobilecoder.com/dependency-injection-in-flutter-using-riverpod/

https://zuma-lab.com/posts/flutter-search-github-riverpod-di