kuwabara tech note

FlutterにおけるRepository Pattern

Repository Patternとは?

アーキテクチャにRepositoryを組み込むパターンです。

Repositoryは、UIやビジネスロジックとは関係ない、ネットワークからデータを所得するような部分です。データの所得だけでなく、データのローカルキャッシングや、取ってきたデータのモデルへの変換等も行います。

Repository Patternのメリット

UI、ビジネスロジックと明確に分けることで、以下のメリットがあります。

  1. データ所得部分の変更をしても他に影響が出ない
  2. 型安全性が保証される

例えばデータベースの構造を変更したことで、データ所得のコードも変更されても、UIやビジネスロジックのコードには影響が出ません。

また、型安全性とはType Errorが起きないということです。関数でreturnする型が保証されるので、間違って別の型やnullが返ってくることはありません。

Repository Patternのデメリット

  1. boilerplate code*が増える
  2. 学習が少し必要?

*boilerplate code ... 複数の場所で使われる定型コード

Repository Patternを実際に使ってみる

以下のFirebaseUsersRepositoryが今回作ったRepositoryのクラスです。

final usersRepositoryProvider =
    Provider<FirebaseUsersRepository>((ref) => FirebaseUsersRepository());

class FirebaseUsersRepository {
  static const int fetchLength = 10;

  Future<List<UserModel>> fetchUsers(
      List<String> genderQuery, List<UserModel> before) async {
    var from = before.isEmpty
        ? Timestamp.fromDate(DateTime.now())
        : before[before.length - 1].timestamp;

    // Firebaseからデータを所得
    QuerySnapshot<Map<String, dynamic>> docs = await FirebaseFirestore.instance
        .collection('users')
        .doc('v1')
        .collection('public')
        .where('query.gender', whereIn: genderQuery)
        .orderBy('timestamp', descending: true)
        .startAfter([from])
        .limit(fetchLength)
        .get();

    // 所得したデータをUserModel型のListに変換
    String userid = FirebaseAuth.instance.currentUser!.uid;
    List<UserModel> newList = [];
    docs.docs.forEach((element) {
      if (element.exists &&
          element.id != userid &&
          element.data().containsKey('id')) {
        newList.add(UserModel(element));
      }
    });

    return newList;
  }
}

Repositoryクラス内の関数fetchUsersでは、Firebaseからユーザーのデータを10個所得し、UserModel型のListで返します。

1行目のusersRepositoryProviderを通してビジネスロジック側がRepositoryを使うことができます。

List<UserModel> userList = await ref
        .read(usersRepositoryProvider)
        .fetchUsers(genderQuery, state.allUsers);

Providerを使っているのは、どこからでもFirebaseUsersRepositoryにアクセスできるようにするためです。

アプリの様々な場所からステートにアクセスできるようになります。 つまり、プロバイダはシングルトンやサービスロケータのようなパターン、依存性注入、あるいはInheritedWidget を完全に代替することができます。 引用元: riverpod.dev

以上です!ありがとうございました。