アプリケーション開発においてNoSQLは使うべきではないのか?
アプリケーション開発において、NoSQLを使うことは選択肢としてどうなのか?
自分としてはNoSQLをメインのデータストアとして利用することは可能であり、その価値があると思っているので、そのことについて記事にしてみる。
経歴
この主張のコンテキストを共有するために、自分の経歴を軽く説明しておく。
自分はFlutterを用いた、モバイル・Webアプリケーションの開発を行う、ソフトウェアエンジニア。
主にFlutterのデータストアにはNoSQLデータベースである、Firestoreを利用してきた。
ここ5年ほどFlutterを利用しており、小規模なチームで5つ程度のアプリを新規開発して、その間に既存のアプリのアップデートやメンテナンスも行なっている。
RDBはRailsでActiveRecordを通して利用したことがある程度で、NoSQLほどの経験があるわけではないが、NoSQLデータベース上で設計する上でも、RDBの設計手法やアンチパターンは役に立つと考え、キャッチアップを行なっている。
スキーマレスの誤解
スキーマレスであることは、データのスキーマをDBが保証してくれないということだ。
これに対して、スキーマレスで嬉しいことなんてないからNoSQLは使えないという意見がある。
確かにこの文面だけ見ると、正しそうに見える。自分もスキーマがなかったり、スキーマがめちゃくちゃなソフトウェアを引き継いでくれといわれたら、嫌だからだ。
しかし、DBがスキーマを保証しないことと、ソフトウェアにスキーマがないことは別の問題である。
NoSQLを使った開発では、ソフトウェアのスキーマを保証するのはDBではなくアプリケーションの役割であり、多くの場合、厳密なスキーマ定義をデータベース側で行わず、アプリケーション側でデータ構造を管理することになる。
アプリケーション側でデータ構造を管理するというのは、つまりドメインモデルをjsonに変換する機能、ひいてはドメインモデルそのものがソフトウェア上での信頼できる唯一のスキーマになるということになる。
データ構造をアプリケーション側で管理できると、ドメインモデルにリスト型やマップ型のような複雑な型を持てるようになり、ドメインモデルの表現力を上げることができる。
これこそがスキーマレスのメリットであると考えている。
N+1問題とリポジトリパターン
NoSQLではデータをJSON形式で保存し、複数回のクエリでデータを取得し、クライアント側で結合する(JOINのような操作を実行する)ことが一般的である。
NoSQLはスキーマの整合性を保証しないだけでなく、JOINについても役割をアプリケーション側に譲っていると言える。
そのために設計されているので、1リクエストに対して数十個程度のクエリを実行する、いわゆるN+1問題にある程度の耐性があるのである。
この性質からNoSQLはリポジトリパターンの採用にも適していると感じる。
RDBではRepositoryクラスを実装して、永続化を抽象化した場合にでも、パフォーマンスを意識した操作を行わなければ、N+1問題が発生しやすくなる。
RepositoryにfindByIdメソッドがあるからといって、findByIdメソッドを何回も呼ぶような処理は避けなければならないだろう。
NoSQLの場合は、findByIdを必要な数だけ呼ぶような実装をすることができ、永続化を隠蔽するというRepositoryの責任にとって都合がいいのである。
もちろん、findByIdみたいな処理を過剰に呼びすぎる場合、パフォーマンスが劣化したり、読み込みコストが高くなってしまう問題はあるため、そのようなデータ構造にならないように、ドメインモデルを設計する必要がある。
NoSQLはトランザクションできないという誤解
主要なNoSQLデータベースでは、トランザクションが実装されていることが多い。FirestoreではACIDトランザクションが実装されており、トランザクションによってデータの一貫性を保証することができる。
実際にFirestoreを使って、アプリ内のポイントの付与や使用に関する機能をトランザクション機能を用いて実装したが、問題なく動作した。
よって、「NoSQLはトランザクションができない」というのは誤解である。ただし、データベースの種類によっては、強い一貫性を保証しないものもある。そのようなデータベースで、ポイントシステムのように強い一貫性が必要な機能を実装する場合には注意が必要である。
一方で、すべてのユースケースにおいて強い一貫性が求められるわけではない。結果整合性で十分なケースも多く存在し、本当に強い一貫性が求められているのかについても考えて、DBを選ぶべきだろう。
運用コストが低い
運用時のコストという観点から見ると、NoSQLは非常に優秀だと言える。これはFirestoreのようなフルマネージドなサービスを前提にしているが、特に感じるのはDBのパフォーマンスのための設定がほとんど不要であるという点だ。
Firestoreの紹介文に「フルマネージドでサーバーレスなデータベースを使用することで、アプリケーション開発に集中できます」と書かれているが、実際にその通りだと実感している。
価格面でもFirestoreは非常に優れている。NoSQLの特徴である水平スケーリングを活かして、読み取りや書き込みの回数に応じて課金される仕組みだ。
このモデルの何が嬉しいかというと、サービスの継続可能性が向上する点である。仮にリリースしたアプリが全く使われなくても、読み取りや書き込みが発生しない限り、データベースの利用料を払う必要がないため、コストを抑えてサービスを継続できる。
また、可用性も非常に高く、データベースの問題でシステムが停止し、メンテナンスが必要になるような事態に今まで遭遇したことはない。
まとめ
NoSQLをメインのデータストアとして採用するのは実用的であり、これからも要件に合う限り、積極的に使っていこうと考えている。
スキーマレスという特性は、ドメインモデル設計と相性がよくドメインモデリングを進めつつ、DBアクセス時にどのくらいの読み取りや書き込みが発生するかも考慮して、総合的にスキーマを決める手法を取ることで、そのメリットを最大限に活かせると感じている。
また、NoSQLはRepositoryパターンとの相性も良く、特にFirestoreのようなフルマネージドなデータベースは、自分にとってアプリ開発に欠かせないツールとなっている。
こうした考え方でNoSQLを使っている開発者もいることが、参考になれば幸いだ。
Discussion