■ 1. はじめにと背景
- DB設計への姿勢:
- 個人的にDBというものに対してあまり興味がない
- 基本的にDB設計は詳しい人に任せたいと思っている
- PoCやスモールスタートでの課題:
- ある程度以上の大きめのシステムならそれで良いがPoCやスモールスタートの場合だとDBだけを切り離せるほど人的リソースを割けないことも多い
- 外部にDBを丸投げしたことにより知見がなくなりDB起因でアプリの機能追加や変更が困難になるといった事態も避けたい
- 記事の目的:
- なるべくアプリ開発の邪魔にならないDB設計を考えておきたいとGeminiくん相手に壁打ちしてみた
- 硬い設計をするのが一般的なマスタ管理に柔軟性の高いシングルテーブル設計を採用しNoSQLの定石であるシングルテーブル設計をPostgreSQLのJSONB機能で実現するアプローチが登場した
■ 2. モダンなアプリ開発に求められるDB設計
- 対象システムの特徴:
- 柔軟でモダンなシステムで規模は大きくないがモダンなシステムをなるべく柔軟に作ることを考える
- 取り敢えずマスタを対象に考えDB設計ではよくマスタとトランザクションに分けて考えるのでまずはマスタについて考える
- シンプルな構造を扱うユーザーの意見を素早く取り入れたいのでエクセルで作ったマスタ管理台帳を簡単に取り込めることを前提にする
- 人間が全体を管理できる程度にシンプルなマスタということ
- 自由度は高く項目の増減や変更の自由度は高くしたいモダンなアプリは変更に強いことが求められる
- 従来のDB設計との違い:
- 従来は多くのシステムのデータベースには巨大なデータを扱える堅牢な設計を求められること多かった
- 今は誰にでも気軽にアプリを開発してデプロイして公開できる時代
- モダンな開発では規模は小さいけど柔軟性が求められる事が多く従来のDB設計では上手くいかない事もある
- アプローチ:
- 従来のDB設計は一旦横においてエクセルで実施したマスタ定義をなるべくそのままシステム化することを考えた
■ 3. GeminiくんのDB設計提案
- 設計の概要:
- マスタの定義をすべて一つのテーブルに集約する
- 1つのテーブルに全ての項目を放り込むという大胆な設計を提案してきた
- よく考えると実際にこれが理にかなっている
- テーブル構成:
- master_type: カテゴリやステータスなどの区分でエクセルのシート名に相当
- code: 一意のキー
- name: 表示名で実質的なValue
- category_path: 文字列による階層表現で例は/家電/冷蔵庫
- metadata: エクセルの残りの列をすべてここに放り込む
■ 4. シンプル構成の利点
- 従来設計の問題点:
- PoCやスモールスタートにおいて従来の1エンティティ=1テーブルという硬い設計は柔軟な開発の足枷になる
- エクセルの管理台帳に1つ列を足したいだけなのにDBのマイグレーションに怯え影響調査に時間を費やすことになる
- シングルテーブル設計の利点:
- テーブルを細かく分けずあらゆるマスタを一つの大きな器に放り込むシングルテーブル設計を選択肢に加える
- これにより構造を固定せずビジネスの変化をそのまま受け止める構成を考えることが出来る
- 比喩:
- 正規化重視の設計は川の流れをガチガチに固める堤防を作るのに対してシングルテーブル設計は広い遊水地を用意するといったところ
■ 5. PostgreSQLでの実装
- NoSQLとの比較:
- シングルテーブルと聞くとDynamoDBのようなNoSQLを連想する方が多い
- 実際クエリの効率化のためにデータを1つのテーブルに集約する手法はNoSQLの定石
- DynamoDBではパーティションキーやソートキーを抽象的に定義することで後からアプリ側でデータを解釈する柔軟性を持たせることが一般的
- DynamoDBの課題:
- 直面するのは検索の壁
- DynamoDBは一部の項目にしかインデックスを作れないしそれを補うGSIなどの機能はとても複雑
- 今回は対象がマスタ管理なので検索の柔軟性はとても重要
- PostgreSQL採用の理由:
- シングルテーブル設計によるマスタ管理を検索能力が高いPostgreSQLで実施することを考える
- PostgreSQLの優位性:
- アドホックな検索への対応力: NoSQLは事前に定義したアクセスパターンには強いが想定外の条件での検索や集計は苦手でPostgreSQLはSQLの強力な表現力でいつでも自由な切り口でデータを抽出できる
- JSONBと高度なインデックス: PostgreSQLのJSONB型はスキーマレスな柔軟性を提供し内部フィールドに対してGINやB-Treeインデックスを後から自由に追加可能
- 複雑な階層やパス検索: /食品/生鮮/野菜のようなパスに基づく検索はPostgreSQLのLIKE検索やltree型を使えばインデックスを効かせつつ爆速実行可能でNoSQLのキー設計で解決するよりも遥かに直感的で強力
- PostgreSQLの位置づけ:
- PostgreSQLをただのRDBではなく強力な検索エンジンを備えたドキュメント・ストアとして使うことでスマートなマスタ定義の管理が実現できる
■ 6. マスタのデータ取得方法
- 懸念への対応:
- テーブルが1つでリレーションもないならアプリ側で扱うときに不便じゃないかという懸念がある
- 実はそんなことはない
- アプローチ:
- DB側でJOINして整えるのではなくDB側で構造化して1発で返すというアプローチを取る
- PostgreSQLの関数活用:
- PostgreSQLにはjson_aggやjsonb_build_objectといった抽出結果をまるごと構造化する強力な関数がある
- これらを使えば複数テーブルを何度も叩く代わりに必要なマスタを1つのJSONオブジェクトとしてアプリに返すことができる
- アプリ側はそれを受け取り起動時や定期的なリフレッシュのタイミングでメモリ上に展開してしまえばいい
- メリット:
- アプリ起動時に全ロード
- メモリ上で高速検索
- DB通信のオーバーヘッドなし
- データ量への懸念の解消:
- マスターのデータを全て渡すのは無駄じゃないのかという懸念がある
- しかし今回はあくまでエクセルで人間が管理できるマスタであることが前提
- 今時のコンピューターやネットワークなら数千件や数万件程度のデータは一瞬で処理出来る
- むしろ通信が1回で完結するので効率よくデータを処理することが可能
- 設計思想:
- DBに知能を持たせるのをやめアプリが使いやすい塊としてデータを扱う
- こうすることでリレーションに頼らないスマートなマスタ取得が実現できる
■ 7. モダン開発との関係
- 従来のアプリ開発の問題:
- 特にマスタに関しては整合性を徹底するためにDBの型や制約をガチガチに固めることが多い
- しかし素早い開発や柔軟な仕様変更を求められるモダンな開発ではこの設計が足枷になってしまうことが多々ある
- DBの位置づけの変更:
- DBは器であってフィルターではない
- JSONBで何でも受け入れる柔軟な土台に徹しデータの解釈や不整合の吸収は変更が容易なアプリ側で対応することも考えるべき
- 健全な関係性:
- アプリが賢く立ち回ることで土台であるDBを自由にする
- この関係性こそがモダンな開発における健全なアプリとDBの関係
■ 8. おわりと次回予告
- 提案の効果:
- PostgreSQLを使ってマスタ定義を自由にすることで柔軟なアプリ開発を爆速で実現出来る
- 残された課題:
- そんなルーズなマスタではトランザクションの整合性が取れないのではという疑問が出るのは当然
- 小規模アプリと言っているけど将来的にマスタが巨大化すると詰むよねという懸念がある
- 次回予告:
- 後編ではこのルーズなPostgresqlを支える整合性を担保するDynamoDBについて語る