ボクココ

サービス開発を成功させるまでの歩み

自前 SaaS における DB 設計サンプル

ども、@kimihom at 長野 です。

f:id:cevid_cpp:20190310175440j:plain

昨年ライブ配信システムの wellcast をリリースし、何社か導入いただいている中でコツコツと改善を繰り返してきている。

www.wellcast.in

そこで現状の SaaS DB 設計(マルチテナントアーキテクチャ)をシェアしつつ、今後の見通しについて記そうと思う。

共有DB

マルチテナントアークテクチャに関して、以下の記事に解説が載っている。

mitomasan.hatenablog.com

wellcast のデータベース設計は、セールスフォースと同じ1つのアプリケーションでデータベースを共有 (最も効率的な真のマルチテナンシー)である。上記記事で言われているように、"顧客データを全て一つのDBに混ぜてしまうのはデータ混濁のリスクが怖い" という点が挙げられるが、現段階ではその問題は一度も起きていないし、今後も共有DBが原因で問題を起こす確率は低いと考える。テーブル関連を全て「契約(Contracts)」に紐付けを行うだけで、その問題は回避できるからである。

Contracts 
1

|    

*                 *
Users          Records

契約に紐づいたレコードを必ず取ってくるようにすれば、それだけで他の契約のデータを取ってきたり削除しちゃったりする恐れはない。全てのソースコードで下記コードの記述をするようになるので、勝手に慣れてきてRecord.all みたいなコードはどんなことがあっても書かなくなる。これが最も効率的で、スケールによる問題が起きづらい構成だと考えている。

@contract = current_user.contract

# 複数 Contracts の場合のサンプル
# session[:contract_id] ||= current_user.contracts.first
# @contract = Contract.find(session[:contract_id])

@contract.records  #=> 対象契約のみの Record 一覧の取得

現状は Contracts と他モデルの関係が1対多だけども、1つのログインで複数の契約に入れるようにするには Contracts と Users の関係を多対多にする必要がある。これは最初に決定すべき重要な項目になるだろう。SaaS では複数契約するパターンが割と多く出てくる。

今後、問題が起きてくるとしたらRecords テーブルが一つのテーブルでデータありすぎってところなんだろうけど、インデックスを適切に貼ればよほどのデータ量にならない限り問題にはならないだろう。

決済関連は Stripe に集約

SaaS の DB設計で頭を悩ますことが多いのが決済関連の話だろう。いつ、いくらで決済して成功/失敗したのか。そのログを管理しなければならない。従来の SaaS 開発では、これら全て自社で設計して開発する必要があったが、Stripe の柔軟な API により、Stripe 側に全て寄せることができるようになった。

SaaS といえば月額500円、みたいな超シンプルな決済方法がまず先に思いつくけど、今後はもっと企業ごとに最適な料金体系を設計する必要が出てくるだろう。例えば使った分だけの従量課金制や、大量に利用する企業へのディスカウントなどである。 Stripe はこれら複雑な料金体系の実装もサポートされている。こうした料金体系の考察に関しては、今月開催される Stripe Connect で「SaaS プライシングの考察 ~実体験を通じて~ 」と題してお話しする予定だ。

TOP - JP_Stripes Connect 2019

私たちが保存すべきは契約ごとの Stripe Customer Id のみである。あとはその情報を元に API を投げる事になる。

ただ全ての決済情報を Stripe に集約させると、ユーザーが決済情報の確認をするためにアクセスの度に Stripe API を呼ぶ必要が出てくる。その頻繁なAPIリクエストに関しては改善が必要だと考えている。一部の情報(ex 今月 決済が成功/失敗など)は自社のDBに保存したほうが効率がいいケースが出てくるだろう。今後顧客が増えてきたとしても、そうした対応によって API リクエストによるサーバー負荷問題は回避できると考えている。わざわざ自前サブスクリプションを定義し直すってことにはならないだろう。

Stripe 側に情報を集約させることで、Stripe Sigma といった決済関連のデータアクセスを効率的に取得・管理できるようになる。これが一つの SaaS 決済関連のベストプラクティスになっていく予感がしている。

stripe.com

終わりに

今回は SaaS 開発で頭を悩ます DB 設計についてご紹介した。

もちろん他にも設計方法が色々あるけども、すぐに効率よくデータを管理したいっていう場合に「1つのアプリケーションでデータベースを共有」する方法はオススメできる。

今後この方法で問題が起きそうな何かがあるように感じられている場合には、ぜひコメントいただきたい。その問題について私は現時点でどう対応しているのか、もしくはその問題を把握していなかったのかを共有できればと思う。

理論から学ぶデータベース実践入門 ~リレーショナルモデルによる効率的なSQL (WEB+DB PRESS plus)

理論から学ぶデータベース実践入門 ~リレーショナルモデルによる効率的なSQL (WEB+DB PRESS plus)