Slide 1

Slide 1 text

Databaseのことを考えずにiOSアプリを作る ローカルデータベースを使うときの アーキテクチャ設計

Slide 2

Slide 2 text

自己紹介 ● ヤズジュムーサー(ムッチョ) ● VoicyのiOSエンジニア ● Androidもぼちぼちやってます ● 趣味はボルダリング Mucchooooo

Slide 3

Slide 3 text

個人開発もやってます!

Slide 4

Slide 4 text

ローカルデータベースで 苦労した経験はありますか?

Slide 5

Slide 5 text

データベースのオブジェクトの扱いには ルールが多い!

Slide 6

Slide 6 text

例えば... Realm このRealmオブジェクトは普通の変数のよう に取得したり変更したりできない。

Slide 7

Slide 7 text

考えなきゃいけない ルール ● Realmインスタンスの扱い方 ● Threadは単一のものを使用しているか ● etc. Realmの場合

Slide 8

Slide 8 text

データベースオブジェクトの扱いに付随するルール はたくさんある Realmの場合 1. threadを跨いでオブジェクトにアクセスするとクラッシュ 2. realm.write {} ブロックの外でrealmオブジェクトを編集するとクラッシュ 3. モデル定義にないプロパティへアクセスしようとするとクラッシュ 4. 削除済みのRealmオブジェクトにアクセスしようとするとクラッシュ 5. Realmインスタンスを適切にクローズしないとリソースのリークや不正な状態の参照でクラッシュ CoreDataの場合 1. Contextを異なるThread間で共有するとクラッシュ 2. フェッチリクエストが完了する前に結果セットにアクセスしようとするとクラッシュ 3. モデル定義にないエンティティにアクセスしようとするとクラッシュ 4. 削除されたか、コンテキストに属していないマネージドオブジェクトを操作するとクラッシュ 5. モデルの属性に対して予期しない型のデータを設定しようとするとクラッシュ 6. コンパイルされたモデルとコード内のモデルが一致しない場合クラッシュ

Slide 9

Slide 9 text

データベースオブジェクトの扱いに付随するルール はたくさんある Realmの場合 1. threadを跨いでオブジェクトにアクセスするとクラッシュ 2. realm.write {} ブロックの外でrealmオブジェクトを編集するとクラッシュ 3. モデル定義にないプロパティへアクセスしようとするとクラッシュ 4. 削除済みのRealmオブジェクトにアクセスしようとするとクラッシュ 5. Realmインスタンスを適切にクローズしないとリソースのリークや不正な状態の参照でクラッシュ CoreDataの場合 1. Contextを異なるThread間で共有するとクラッシュ 2. フェッチリクエストが完了する前に結果セットにアクセスしようとするとクラッシュ 3. モデル定義にないエンティティにアクセスしようとするとクラッシュ 4. 削除されたか、コンテキストに属していないマネージドオブジェクトを操作するとクラッシュ 5. モデルの属性に対して予期しない型のデータを設定しようとするとクラッシュ 6. コンパイルされたモデルとコード内のモデルが一致しない場合クラッシュ ルールを守るのは 難しい・苦労する

Slide 10

Slide 10 text

ルールに違反していても、コンパイルエ ラーが起きない上、コードから違反箇所を 見つけることは難しい。 開発者かユーザーがクラッシュするまで 見つけられない可能性も

Slide 11

Slide 11 text

データベースオブジェクトの ルールは 品質的にも脅威 開発効率的にも脅威

Slide 12

Slide 12 text

データベースのオブジェクトを 普通の変数のように自由に扱いたい!

Slide 13

Slide 13 text

Solution 1.データベース用のオブジェクトと普通のオ ブジェクトを用意する Normal Object Realm Object

Slide 14

Slide 14 text

2.オブジェクト同士を 変換できる状態にする Normal Object Realm Object

Slide 15

Slide 15 text

3. Realm Serviceを用意 データベースに関わる処理を担当 Realmに依存しないCard配列を 他の全てのファイルで使う

Slide 16

Slide 16 text

4.アプリ起動時にRealm ObjectをFetchして Normal Objectに変換 RealmCard情報をfetchして Card配列に変換

Slide 17

Slide 17 text

Card配列に少しでも変更があると syncronizeWithRealm() が呼ばれる バックグラウンドスレッドにて Card配列をRealmCardに変換し、 情報をRealmデータに適用 4.Normal Objectが変更された時 Realm Objectが自動で変更される状態を作る

Slide 18

Slide 18 text

普通の使い方の場合 Realm Card

Slide 19

Slide 19 text

普通の使い方の場合 Realm Cardを扱う全てのファイル (View, ViewModel, Testファイルなど) がRealmに依存する+ルールに従う義務を負う Realm Card

Slide 20

Slide 20 text

今回のアーキテクチャの場合 起動時のFetch・Cardへの変換 Cardが変更されるたび自動でRealmに保存 Realm Card Card Realm Service

Slide 21

Slide 21 text

今回のアーキテクチャの場合 起動時のFetch・Cardへの変換 Cardが変更されるたび自動でRealmに保存 アプリ全体ではRealmに一切依存しないCardクラスが使われるため、 RealmService以外の全てのファイルがRealmに依存しない。 Realmのことを一切考えずに開発ができる! Realm Card Card Realm Service

Slide 22

Slide 22 text

今回紹介したアーキテクチャの メリット 1. 保守性・開発効率の向上! ルールから脱却!データベースのことを考えずに開発ができる! 2. 最高レベルのUIパフォーマンスを達成できる! データの更新は常にバックグラウンド。 データ量が膨大になるとローカルデータベースでも重くなりがちだが、いつでも超高速 3. テストも書きやすくなる+動作も高速! テストの実行がデータベースに依存しない 。パラレル実行なども簡単。 4. コード全体がデータベースに依存しない。 もしデータベースを変更するとしても書き直しが少ない。

Slide 23

Slide 23 text

まとめ データベースのオブジェクトは デリケートなアイテム 直に触れることは避けるのがベター

Slide 24

Slide 24 text

デメリット、コード、詳しい解説は Zennにあります! https://zenn.dev/voicy/articles/dd42bfbee7de01

Slide 25

Slide 25 text

ありがとうございました!