FlatStore → VergeORM

既存のVergeから新しいVergeへ

FlatStoreとは

永続化しないDB?

DBって?

  • テーブル
  • エンティティ
  • クエリ
  • 再利用しやすい形
    • 正規化的な

→ 正規化してデータを格納しておく場所?

正規化(Normalize)とは

正規化されていない状態のJSON(Denormalized)

const blogPosts = [
  {
    id: 'post1',
    author: { username: 'user1', name: 'User 1' },
    body: '......',
    comments: [
      {
        id: 'comment1',
        author: { username: 'user2', name: 'User 2' },
        comment: '.....'
      },
      {
        id: 'comment2',
        author: { username: 'user3', name: 'User 3' },
        comment: '.....'
      }
    ]
  },
  {
    id: 'post2',
    author: { username: 'user2', name: 'User 2' },
    body: '......',
    comments: [
      {
        id: 'comment3',
        author: { username: 'user3', name: 'User 3' },
        comment: '.....'
      },
      {
        id: 'comment4',
        author: { username: 'user1', name: 'User 1' },
        comment: '.....'
      },
      {
        id: 'comment5',
        author: { username: 'user3', name: 'User 3' },
        comment: '.....'
      }
    ]
  }
  // and repeat many times
]

正規化されいているJSON(Normalized)

Normalized

{
    posts : {
        byId : {
            "post1" : {
                id : "post1",
                author : "user1",
                body : "......",
                comments : ["comment1", "comment2"]
            },
            "post2" : {
                id : "post2",
                author : "user2",
                body : "......",
                comments : ["comment3", "comment4", "comment5"]
            }
        },
        allIds : ["post1", "post2"]
    },
    comments : {
        byId : {
            "comment1" : {
                id : "comment1",
                author : "user2",
                comment : ".....",
            },
            "comment2" : {
                id : "comment2",
                author : "user3",
                comment : ".....",
            },
            "comment3" : {
                id : "comment3",
                author : "user3",
                comment : ".....",
            },
            "comment4" : {
                id : "comment4",
                author : "user1",
                comment : ".....",
            },
            "comment5" : {
                id : "comment5",
                author : "user3",
                comment : ".....",
            },
        },
        allIds : ["comment1", "comment2", "comment3", "commment4", "comment5"]
    },
    users : {
        byId : {
            "user1" : {
                username : "user1",
                name : "User 1",
            },
            "user2" : {
                username : "user2",
                name : "User 2",
            },
            "user3" : {
                username : "user3",
                name : "User 3",
            }
        },
        allIds : ["user1", "user2", "user3"]
    }
}

Normalizeの必要性

  • パフォーマンス
    • 計算量
      • 更新するときに、O(1)で済むように
    • メモリ
      • 同じデータが存在しないように

      Partnerが参加するコミュニティがParnterに埋め込まれていると、同じコミュニティのデータが複製されまくる。

      ほぼ全員が入るようなコミュニティAがあったとして、Partner1000人取得したら、コミュニティAのデータも1000件。

      コミュニティに参加したら1000件の参加プロパティを更新しなければならない O(n)

      さらにどのコミュニティAが正しいデータなのかはわからない。

      → Source of truth

      信頼できるデータはどれなのかを知りやすくしておかなければならない。

  • Partner
    • [Community]

Normalizeするデメリット

  • リレーションシップのオブジェクトがOptionalになる
    • NSManagedObjectのリレーションシップがOptionalの理由はこれ。
  • データ更新する時もNorrmalizeを意識しないといけない
let fetched: [Book] 

var store: [Book.ID : Book] = ...

// insert
for item in fetched {
  store[item.id] = item
}
  • 手間はかかる

ORMを利用すると手間がすこし楽になる(原理を知らなくて良いわけではない)

ORM = Object Relational Mapping

正規化されたデータを扱いやすくするレイヤー

  • データ、CRUDのサポート
    • C : Create
    • R : Read
    • U : Update
    • D : Delete
  • 更新の購読
    • 次に相当
      • CoreData.NSFetchedResultController
      • Realm.Results<T>

💡

このあたりからFluxの話につながっていく

Fluxで扱うStateとEntityを同じものとして扱いたい。

アプリが持つEntityも状態

今まで

  • Application State (Storage, Verge) on Service
    • ...
  • Entity(FlatStore)
    • ...

これから

  • Application State
    • State
    • Entity

Getterの必要性

Fluxの実装モデルであるReduxを見ると、Single state treeというワードを見かける。

これはアプリケーションの状態を同じルートをもつ値型のツリー構造で管理しましょう。というもの。

管理しましょう。というだけで、それをそのまますべてのコンポーネントに渡すもの。とは表現していません。

鋭いエンジニアだと、コンポーネントに必要のないものまで渡してしまうのは気が引けると思います。

そこで、Reduxは reselect というライブラリを公開しています。

Getterはそれです。

Memoization

(Input) -> Output

Inputが変わらなければOutputが変わらないので、InputをdistinctUntilChangedすればパフォーマンスアップできる。というもの。