Flutterアプリケーション開発概論

【データ構造】users・teams・members・tasksのつながりを理解する

18LINE風チームタスク管理アプリを作りながら、ログイン・データベース・権限管理を学ぶ
FlutteriOSAndroidMacOSWindows基礎から学ぶ開発アプリ開発

このページでやること

このページでは、Firestoreに保存するデータの形を整理します。

Firestoreとは、Firebaseのデータベースです。

データベースとは、アプリの情報を保存する場所です。

今回のアプリでは、主に次の4つを使います。

データ名一言説明
usersアプリを使う人のプロフィール
teamsタスクを共有するチーム
membersチームに参加している人
tasksチーム内で管理する作業

このページではコードをたくさん書きません。

次のチーム作成に進む前に、「どこに何を保存するのか」を確認します。


今日のゴール

今日のゴールは、次の形を理解することです。

users
  └ uid

teams
  └ teamId
      ├ members
      │   └ uid
      └ tasks
          └ taskId

最初は難しく見えるかもしれません。

でも、短く言うとこうです。

users
↓
ユーザー情報

teams
↓
チーム情報

teamsの中のmembers
↓
そのチームに参加している人

teamsの中のtasks
↓
そのチームのタスク

npmや環境変数はこのページで必要?

このページでは、npmは使いません。

環境変数も設定しません。

環境変数とは、パソコン全体で使う設定値のことです。

このページでやることは、Firestoreのデータ構造を理解することです。

Firestoreの保存場所を見る
↓
users・teams・members・tasksの役割を知る
↓
次のチーム作成に備える

まずFirestoreの基本を確認する

Firestoreでは、データを次の形で保存します。

コレクション
  └ ドキュメント
      └ フィールド

それぞれ一言で説明します。

単語一言説明
コレクション同じ種類のデータをまとめる箱
ドキュメント1件分のデータ
フィールドドキュメントの中に入る項目
サブコレクションドキュメントの中に作る小さなコレクション

たとえば、ユーザー情報ならこうです。

users
  └ abc123
      ├ displayName
      ├ email
      └ department

この場合、

users

がコレクションです。

abc123

がドキュメントです。

displayName
email
department

がフィールドです。


今回の全体構造

今回のアプリでは、Firestoreを次の形で使います。

users
  └ uid

teams
  └ teamId
      ├ members
      │   └ uid
      └ tasks
          └ taskId

これを日本語で書くと、こうです。

ユーザー一覧
  └ ユーザー1人分

チーム一覧
  └ チーム1つ分
      ├ メンバー一覧
      │   └ メンバー1人分
      └ タスク一覧
          └ タスク1件分

つまり、チームの中に、そのチームのメンバーとタスクを入れます。


usersとは何か

users は、アプリを使うユーザーのプロフィールを保存する場所です。

プロフィールとは、名前や所属など、その人に関する情報のことです。

保存場所は次の形です。

users/{uid}

uid とは、Firebase Authenticationがユーザーごとに発行するIDです。

たとえば、山田太郎さんの uidabc123 なら、保存場所はこうなります。

users/abc123

中身は、たとえば次のようになります。

users/abc123
  displayName: 山田太郎
  email: yamada@example.com
  department: 開発チーム
  photoUrl:
  defaultRole: member
  createdAt: 作成日時
  updatedAt: 更新日時

usersに保存する項目

users/{uid} に保存する主な項目は、次の通りです。

項目一言説明
displayName画面に表示する名前
emailログインやメンバー検索に使うメール
department所属・部署・学校名など
photoUrlプロフィール画像のURL。今回は空文字
defaultRole初期の役割。今回は member
createdAt作成日時
updatedAt更新日時

URLとは、画像やページなどの場所を表す文字列です。

今回は画像機能を作らないので、photoUrl は空のままで進めます。


teamsとは何か

teams は、タスクを共有するチームを保存する場所です。

チームとは、同じタスクを一緒に管理するグループのことです。

保存場所は次の形です。

teams/{teamId}

teamId とは、チームごとに作られるIDです。

たとえば、開発チームを作ると、Firestore上では次のようになります。

teams/team001

中身は、たとえば次のようになります。

teams/team001
  name: 開発チーム
  ownerId: abc123
  memberIds: [abc123]
  createdAt: 作成日時
  updatedAt: 更新日時

teamsに保存する項目

teams/{teamId} に保存する主な項目は、次の通りです。

項目一言説明
nameチーム名
ownerIdチームを作った人のuid
memberIds参加メンバーのuid一覧
createdAt作成日時
updatedAt更新日時

memberIds は、とても大切です。

これは、そのチームに参加しているユーザーの uid を配列で保存します。

配列とは、複数の値をまとめたリストのことです。

memberIds: [abc123, xyz789]

このように保存しておくと、「自分が参加しているチームだけ」を取り出しやすくなります。


なぜmemberIdsが必要なのか

トーク一覧画面では、自分が参加しているチームだけを表示したいです。

たとえば、山田さんの uidabc123 だったとします。

その場合、Firestoreから次のように探します。

memberIdsにabc123が含まれるチームだけ表示する

Flutterでは、あとで次のような検索を使います。

.where('memberIds', arrayContains: uid)

arrayContains は、配列の中に指定した値が含まれているか調べるFirestoreの検索条件です。

これによって、ログイン中のユーザーが参加しているチームだけを表示できます。


membersとは何か

members は、あるチームに参加しているメンバーを保存する場所です。

メンバーとは、チームに参加しているユーザーのことです。

保存場所は次の形です。

teams/{teamId}/members/{uid}

たとえば、開発チーム team001 に、山田さん abc123 が参加している場合はこうなります。

teams/team001/members/abc123

中身は、たとえば次のようになります。

teams/team001/members/abc123
  uid: abc123
  email: yamada@example.com
  displayName: 山田太郎
  role: owner
  joinedAt: 参加日時

membersに保存する項目

teams/{teamId}/members/{uid} に保存する主な項目は、次の通りです。

項目一言説明
uidメンバーのユーザーID
emailメンバーのメールアドレス
displayNameメンバーの表示名
roleチーム内での権限
joinedAtチームに参加した日時

role とは、その人が何をできるかを表す権限です。

権限とは、操作できる範囲を決めるルールです。

今回のアプリでは、次の4つを使います。

権限一言説明
ownerチームの所有者。すべて操作できる
admin管理者。メンバー追加やタスク削除ができる
member一般メンバー。タスク作成や編集ができる
viewer閲覧のみ

なぜmembersとmemberIdsの両方が必要なのか

ここは少し大切です。

今回のアプリでは、teams の中に memberIds を保存します。

さらに、teams/{teamId}/members/{uid} にもメンバー情報を保存します。

「同じような情報を2か所に持つの?」と思うかもしれません。

理由は、役割が違うからです。

データ役割
memberIds自分が参加しているチームを探すため
membersメンバーの名前・メール・権限を管理するため

memberIds は検索用です。

members は管理用です。

このように、Firestoreでは検索しやすくするために、必要な情報を少し重複して持つことがあります。

重複とは、同じ情報を複数の場所に持つことです。


tasksとは何か

tasks は、チーム内のタスクを保存する場所です。

タスクとは、やるべき作業のことです。

保存場所は次の形です。

teams/{teamId}/tasks/{taskId}

たとえば、開発チーム team001 に「ログイン画面を作る」というタスクを作ると、こうなります。

teams/team001/tasks/task001

中身は、たとえば次のようになります。

teams/team001/tasks/task001
  title: ログイン画面を作る
  description: メールとパスワードの入力欄を作る
  status: todo
  priority: high
  assigneeEmail: yamada@example.com
  assigneeId: abc123
  createdBy: abc123
  createdAt: 作成日時
  updatedAt: 更新日時

tasksに保存する項目

teams/{teamId}/tasks/{taskId} に保存する主な項目は、次の通りです。

項目一言説明
titleタスク名
descriptionタスクの説明
status進行状況
priority優先度
assigneeEmail担当者メール
assigneeId担当者のuid
createdBy作成者のuid
createdAt作成日時
updatedAt更新日時

status は、タスクの進行状況です。

今回のアプリでは、次の3つを使います。

status表示名
todo未対応
doing進行中
done完了

priority は、優先度です。

優先度とは、どのタスクを先に対応するべきかを表すものです。


なぜtasksをteamsの中に入れるのか

タスクは、チームごとに分けたいです。

たとえば、開発チームと営業チームがあるとします。

開発チームのタスク
  ├ ログイン画面を作る
  └ Firestore接続を確認する

営業チームのタスク
  ├ 見積書を送る
  └ 打ち合わせ日程を確認する

これが混ざると困ります。

そこで、タスクはチームの中に保存します。

teams/{teamId}/tasks/{taskId}

こうすると、チームを開いたときに、そのチームのタスクだけを表示できます。


4つのデータのつながり

ここまでの内容を、もう一度つなげて見ます。

users/{uid}
↓
ユーザーのプロフィール

teams/{teamId}
↓
チームの基本情報

teams/{teamId}/members/{uid}
↓
そのチームに参加している人と権限

teams/{teamId}/tasks/{taskId}
↓
そのチームのタスク

実際のアプリの流れにすると、こうです。

ユーザーが新規登録する
↓
users/{uid} にプロフィール保存

ユーザーがチームを作る
↓
teams/{teamId} を作成
↓
teams/{teamId}/members/{uid} にownerとして保存
↓
teams/{teamId}.memberIds にuidを入れる

チームを開く
↓
teams/{teamId}/tasks からタスクを取得する

メンバーを追加する
↓
usersからメールで検索
↓
teams/{teamId}/members/{uid} に追加
↓
teams/{teamId}.memberIds にuidを追加

画面とデータの対応

画面ごとに使うデータを整理します。

画面使うデータ
新規登録画面users/{uid}
トーク一覧画面teams
チーム作成画面teams/{teamId}members/{uid}
タスク一覧画面teams/{teamId}/tasks
タスク作成画面teams/{teamId}/tasks/{taskId}
メンバー管理画面teams/{teamId}/membersusers
メンバー追加画面usersmembersmemberIds

「どの画面が、どのデータを使っているか」を見ておくと、コードが読みやすくなります。


Firestore Consoleで見る場所

Firebase Consoleでは、次の順番で確認できます。

Firebase Console
↓
Firestore Database
↓
データ

そこで、次のような構造を確認します。

users
teams

teams の中の1つのチームを開くと、サブコレクションとして次が見えます。

members
tasks

サブコレクションとは、ドキュメントの中に作るコレクションのことです。


次のページで使うチーム作成の考え方

次のページでは、チーム作成を作ります。

チームを作るときは、1つのデータだけではなく、2つの場所に保存します。

teams/{teamId}
teams/{teamId}/members/{uid}

さらに、teams/{teamId} の中に memberIds も入れます。

memberIds: [ログイン中ユーザーのuid]

つまり、チーム作成では、次の3つを同時に作ります。

1. チーム情報を作る
2. 作成者をメンバーとして登録する
3. 作成者のuidをmemberIdsに入れる

これが分かっていれば、次のチーム作成コードがかなり読みやすくなります。


まず覚える形

このページで全部を暗記する必要はありません。

まずは、これだけ覚えてください。

users/{uid}
↓
ユーザー情報

teams/{teamId}
↓
チーム情報

teams/{teamId}/members/{uid}
↓
チームのメンバー情報

teams/{teamId}/tasks/{taskId}
↓
チームのタスク情報

この4つが今回のアプリの土台です。


よくある混乱ポイント

混乱すること考え方
usersmembers の違いusers は全体のユーザー情報、members はチーム内の参加情報
uidteamId の違いuid はユーザーID、teamId はチームID
なぜ memberIds が必要か自分の参加チームを検索しやすくするため
なぜ tasksteams の中に入れるかチームごとにタスクを分けるため
なぜ同じメールを複数場所に持つか画面表示や検索を簡単にするため

ミニ確認問題

Q1. users/{uid} には何を保存しますか?

回答

ユーザーのプロフィールを保存します。

たとえば、名前、メールアドレス、所属、作成日時などです。


Q2. teams/{teamId} には何を保存しますか?

回答

チームの基本情報を保存します。

たとえば、チーム名、作成者のuid、参加メンバーのuid一覧、作成日時などです。


Q3. teams/{teamId}/members/{uid} には何を保存しますか?

回答

そのチームに参加しているメンバー情報を保存します。

たとえば、uid、メールアドレス、表示名、権限、参加日時などです。


Q4. teams/{teamId}/tasks/{taskId} には何を保存しますか?

回答

そのチーム内のタスク情報を保存します。

たとえば、タスク名、説明、進行状況、優先度、担当者、作成者、作成日時などです。


このページのまとめ

  • このページでは、Firestoreのデータ構造を整理した。
  • users は、アプリを使うユーザーのプロフィールを保存する場所。
  • teams は、チーム情報を保存する場所。
  • members は、チームに参加している人と権限を保存する場所。
  • tasks は、チーム内のタスクを保存する場所。
  • memberstasks は、teams/{teamId} の中に作る。
  • memberIds は、自分が参加しているチームを探しやすくするために使う。
  • usersmembers は役割が違う。
  • users は全体のユーザー情報。
  • members はチーム内での参加情報。
  • このページではnpmや環境変数は不要。
  • 次のページでは、この構造を使ってチーム作成を作る。

次のページでやること

次のページでは、チーム作成機能を作ります。

teams/{teamId}teams/{teamId}/members/{uid} を同時に作り、ログイン中のユーザーを自動で owner として登録します。

教材トップへ戻る