FirebaseでModelを作るとダメな理由
Firebase Realtime DatabaseはスキーマレスなDBです。
NoSQLに慣れていない人には、扱いやすいようで扱いにくいデータベースだと思います。さらにFirebaseはMongoDBのようなスキーマレスだけどモデルを定義できるデータベースと違い全くモデルを定義する機能を持っていません。なぜでしょうか。この記事ではなぜモデルを持たない方がいいのかを解説します。
モデルがあるって最高!?
スキーマレスのデータベースが扱いやすいと思うポイント
- どんな形式でも受け入れてくれる
- ネストって最高
スキーマレスのデータベースが扱いにくいと思うポイント
- 今何を触ってるのかわからない感
- 補完が効いてくれない恐怖
きっともっとまともな理由はすぐに想像つくと思いますが、モデルを作ることでいいこともあれば、それによって縛られることもある訳です。
現実的に、このように書くより
Database.database().reference()
.child("user")
.child("1")
.child("name")
.set("Firebase Man")
こう書きたいと思います。
let user = User()
user.name = "Firebase Man"
user.save()
なぜモデルを提供しないんでしょうか?その理由は、スキーマレスそのものにあると考えられます。以下のユーザー更新を行うフロウを見て下さい。
1. まずは、ユーザーをINSERT
USER TABLE
ID | NAME | AGE |
---|---|---|
0 | Firebase | 3 |
GROUP TABLE
ID | NAME |
---|---|
0 |
USER_GROUP TABLE
ID | USER_ID | GROUP_ID |
---|---|---|
0 | 0 | 0 |
MySQLならこんな感じ
INSERT INTO USER (NAME, AGE)
VALUES ("Firebase", 3)
INSERT INTO GROUP (NAME)
VALUES ("Google")
INSERT INTO USER_GROUP (USER_ID, GROUP_ID)
VALUES (0, 0)
Firebaseならこんな感じ
// Userを作る
let user = ["0": [
"name": "Firebase",
"age": "3",
"groupIDs": [
0: true //UserとGroupの相互リレーション
]
]]
Database.database().reference()
.child("user")
.set(user)
// Groupを作る
let group = ["0": [
"name": "Google",
"userIDs": [
0: true //UserとGroupの相互リレーション
]
]]
Database.database().reference()
.child("group")
.set(group)
2. 次にユーザーを更新
AGEを3
->300
にます。
MySQLならこんな感じ
INSERT INTO USER (NAME, AGE)
VALUES ("Firebase", 300, 0)
Firebaseならこんな感じ
let user = ["0": [
"name": "Firebase",
"age": "3",
"groupIDs": [
0: true //UserとGroupの相互リレーション
]
]]
Database.database().reference()
.child("user")
.set(user)
ここが問題です。Firebase
では次のようにプロパティを明確に指定して更新する必要があります。
Database.database().reference()
.child("user")
.child("0") // userID
.child("age") // property
.set(300)
なぜ?
FirebaseではN:Nのリレーションに相互参照できるようにデータを保持します。「?」となった方はこちらへ
前者の更新方法で、二つの端末から同時に更新リクエストがあった場合を考えて見ましょう。
最初のリクエストがgroupIDsに新しいGroupをインサートする処理だったとします。
let user = ["0": [
"name": "Firebase",
"age": "3",
"groupIDs": [
0: true, //UserとGroupの相互リレーション
1: true
]
]]
Database.database().reference()
.child("user")
.set(user)
次にほんの少し遅れたタイミングでAGEを3
->300
にするリクエストがきたとします。
let user = ["0": [
"name": "Firebase",
"age": "300",
"groupIDs": [
0: true //UserとGroupの相互リレーション
]
]]
Database.database().reference()
.child("user")
.set(user)
見事にgroupIDsが上書きされました。Firebaseでモデルを扱うと言うことは、こう言うことです。
でも大丈夫‼️我々にはSaladaがある! (iOSだけ)
Salada
はFirebase Realtime Databaseをモデルで扱うことができるFrameworkです。
こんな感じでGroup
とUser
を扱えます。
let group: Group = Group()
group.name = "iOS Development Team"
group.save { (error, ref) in
do {
let user: User = User()
user.name = "john appleseed"
user.age = 22
user.groups.insert(group.id)
user.save({ (error, ref) in
group.users.insert(ref.key) // It is updated automatically
})
}
do {
let user: User = User()
user.name = "Marilyn Monroe"
user.age = 22
user.groups.insert(group.id)
user.save({ (error, ref) in
group.users.insert(ref.key) // It is updated automatically
})
}
}
Saladaは、Save
が一度しかできません。更新するためにUpdate
も用意されていません。値をセットするだけでデータベースもリアルタイムに更新します。例えばユーザーを更新するならこんな感じ
User.observeSingle("userID", eventType: .value) { (user) in
user?.name = "firebase" // 値をセット瞬間にリアルタイムでオンラインのデータベースを更新します。
}
※ オフラインであっても、値を保持してオンラインになった瞬間に更新します。
SaladaではModelベースで値を更新していません。プロパティベースで値を更新します。