FirebaseでModelを作るとダメな理由

# FirebaseでModelを作るとダメな理由

Firebase Realtime DatabaseはスキーマレスなDBです。
NoSQLに慣れていない人には、扱いやすいようで扱いにくいデータベースだと思います。さらにFirebaseはMongoDBのようなスキーマレスだけどモデルを定義できるデータベースと違い全くモデルを定義する機能を持っていません。なぜでしょうか。この記事ではなぜモデルを持たない方がいいのかを解説します。

## モデルがあるって最高！？

__スキーマレスのデータベースが扱いやすいと思うポイント__

- どんな形式でも受け入れてくれる
- ネストって最高

__スキーマレスのデータベースが扱いにくいと思うポイント__

- 今何を触ってるのかわからない感
- 補完が効いてくれない恐怖

きっともっとまともな理由はすぐに想像つくと思いますが、モデルを作ることでいいこともあれば、それによって縛られることもある訳です。

現実的に、このように書くより

``` Swift
Database.database().reference()
.child("user")
.child("1")
.child("name")
.set("Firebase Man")
```

こう書きたいと思います。

``` Swift
let user = User()
user.name = "Firebase Man"
user.save()
```

なぜモデルを提供しないんでしょうか？その理由は、スキーマレスそのものにあると考えられます。以下のユーザー更新を行うフロウを見て下さい。

### 1. まずは、ユーザーをINSERT

__USER TABLE__

| ID | NAME | AGE | 
| --- | --- | --- |
| 0 | Firebase | 3 |

__GROUP TABLE__

| ID | NAME |
| --- | --- |
| 0 | Google |

__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のリレーションに相互参照できるようにデータを保持します。「？」となった方は[こちら](http://qiita.com/1amageek/items/64bf85ec2cf1613cf507)へ

前者の更新方法で、二つの端末から同時に更新リクエストがあった場合を考えて見ましょう。
最初のリクエストが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だけ）

![logo.png](https://qiita-image-store.s3.amazonaws.com/0/80287/5598dea1-9070-13cd-6033-b5627f7cad9a.png)

https://github.com/1amageek/Salada

`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ベースで値を更新していません。プロパティベースで値を更新します。