料金をページごとに書くのをやめた|複雑な料金情報をJSONで一元管理した話
料金一覧・シミュレーター・割引計算などで同じ料金を個別管理していた状態から、pricing_master.jsonへ料金情報を一元化した実例。数字のズレを防ぐためのデータ設計と実装を紹介します。
Webサイトで料金を扱うとき、最初はページに直接金額を書いてしまうことがあります。
料金が1つや2つなら、それでもそれほど困りません。
ただ、運営している「ABCクッキング攻略ノート」では、
- コースごとの料金
- 回数ごとの料金
- 通常料金
- ライセンス保有者料金
- 学割
- 入会金
- セット割引
- 分割払い手数料
- 旧料金
など、扱う金額がかなり増えてきました。
その結果、問題になったのが、
同じ料金を複数のページや計算処理に書くこと
でした。
そこで、料金情報を pricing_master.json にまとめ、サイト全体から同じデータを参照する構成へ変更しました。
今回は、なぜこの構成に変えたのかと、実際にどう管理しているのかをまとめます。
料金情報が増えると「どこを直せばいいか」が分からなくなる
最初は料金一覧ページだけに金額があれば十分でした。
ところがサイトを作り込んでいくと、同じ料金がいろいろな場所で必要になります。
たとえばABCクッキング攻略ノートでは、
- 料金一覧
- 料金シミュレーター
- 解約時の計算
- ライセンス取得の費用対効果
- 入会時の初期費用計算
などで料金を使います。
ここで、それぞれのページに、
13,200円
165,000円
6,600円
と直接書いていると、料金改定があったときに全部探して修正しなければなりません。
1箇所でも直し忘れると、
料金一覧では新価格なのに、シミュレーターでは旧価格
という状態が起きます。
料金を扱うサイトでは、これはかなりまずい問題です。
実際に料金データが複数に分かれていた
以前は、料金マスタとは別に料金シミュレーター用のJSONも存在していました。
つまり、
「料金を表示するためのデータ」
と、
「料金を計算するためのデータ」
が別々に存在していました。
同じサービスについての数字なのに、用途ごとにデータを持っていたわけです。
これだと更新時に、
「料金マスタは直したけど、シミュレーター側を直していなかった」
という事故が起こりやすくなります。
そこで、シミュレーター専用の料金データを廃止し、料金情報をひとつのマスタへ統合しました。
pricing_master.jsonを料金の基準にした
現在は、
data/pricing_master.json
を料金情報の基準にしています。
ここには、各コースの料金だけではなく、
- 通常入会金
- 学割適用時の入会金
- ライセンス取得料
- 解約事務手数料
- セット割引
- 分割払いの手数料
- コース料金
- 有効期限
- 旧料金
などをまとめています。
たとえばコース料金は、
- カテゴリ
- コース種別
- 契約回数
- 料金
- 有効期限
をセットで持たせています。
さらに必要なコースでは、通常料金とは別にライセンス保有者料金も持たせています。
単純な「商品名と価格」の一覧ではなく、
料金計算に必要な条件そのものをデータ化する
形にしました。
金額だけでなく、割引条件もJSONへ入れた
少し面白いのが、セット割引です。
割引額だけを、
6,600円引き
として持っても、計算には使えません。
「どの組み合わせなら6,600円引きなのか」
も必要だからです。
そこで、セット割については、
- 対象カテゴリ
- 1つ目のコース
- 1つ目の回数
- 2つ目のコース
- 2つ目の回数
- 割引額
までデータとして持たせています。
計算側では、その条件に一致するコースが選択されているかを見て割引を適用します。
こうしておくと、
「画面には6,600円と書いてあるけれど、計算ロジックには別の数字が書いてある」
という状態を避けられます。
JSONを直接あちこちから読む形にもしていない
料金を1つのJSONにまとめても、各ページから直接JSONの構造を触り始めると、今度は別の問題が出ます。
JSONの構造を変えたときに、利用している全ページを修正する必要があるからです。
そのため、ABCクッキング攻略ノートでは、
lib/pricing-master.ts
を料金データへの窓口にしています。
ここで、
- 入会金
- 学割入会金
- ライセンス料
- 解約事務手数料
- セット割
- 分割払い手数料
などを定数として公開しています。
各ページは、
「13,200」
という数字を直接持つのではなく、
ADMISSION_FEE
のような値を参照します。
料金の取得や比較についても、
- コース一覧を取得する
- 指定回数の料金を取得する
- 1回あたり料金を計算する
- 最安プランを探す
- 旧価格と新価格の差を取得する
といった処理を共通化しています。
料金一覧とシミュレーターが同じマスタを見るようになった
この変更で特に重要だったのが、料金一覧と料金シミュレーターです。
以前はシミュレーター専用の料金JSONがありました。
現在はそれを削除し、シミュレーターも pricing_master.json を参照します。
つまり、
ユーザーが一覧で見る料金と、シミュレーターが計算に使う料金の出どころが同じ
になりました。
料金ページだけ更新して計算結果が古いまま、というズレを防ぎやすくなります。
料金改定にも対応しやすくなった
この構成は料金改定のときにも役立ちました。
料金が変わったプランについては、新料金だけでなく old_price も保持しています。
すると、
- 旧価格
- 新価格
- 値上げ額
- 値上げ率
といった情報を、同じ料金マスタから計算できます。
料金改定の記事を書くときに、記事本文へ新旧価格をすべて手入力するのではなく、データから変更対象を取り出せるようになります。
料金一覧と料金改定記事で別々の数字を管理しなくていいのもメリットでした。
一元管理しても、数字が正しいとは限らない
ただし、料金を一元管理すればすべて解決するわけではありません。
間違った料金をマスタに登録すれば、
サイト全体へ間違った数字が一斉に反映されます。
これは一元管理のデメリットでもあります。
そのため、
「どこに数字を書くか」
と、
「その数字が正しいか」
は別の問題として考える必要があります。
料金マスタはあくまで、
確認済みの料金情報をサイト全体で一貫して使うための仕組み
です。
情報源そのものの確認は別途必要です。
この問題は、AIを使って料金情報を確認した際に実際に経験しました。
これは別の記事で詳しくまとめます。
コンテンツサイトでもデータ設計は重要だった
ABCクッキング攻略ノートは、一般的な業務システムではなく情報サイトです。
それでも、料金やキャンペーンのような構造化できる情報が増えてくると、
記事本文へ数字を直接書き続けるだけでは管理しづらくなります。
特に、
- 同じ数字を複数ページで使う
- 条件によって料金が変わる
- シミュレーターでも使う
- 定期的に料金改定がある
というサイトでは、コンテンツとデータを分離するメリットが大きくなります。
今回の構成にしてからは、
「この料金を変更するとき、どのページを探せばいいんだっけ?」
ではなく、
「まず料金マスタを確認する」
という基準を作ることができました。
大事だったのはJSONにしたことではない
今回一番重要だったのは、JSONを使ったこと自体ではありません。
JSONでもデータベースでもCMSでも、手段は何でもいいと思います。
重要なのは、
同じ事実を複数箇所で管理しないこと
でした。
料金一覧、シミュレーター、計算処理、記事。
それぞれが別々に料金を持つのではなく、ひとつの基準データを参照する。
サイトが大きくなるほど、この差は効いてきます。
ABCクッキング攻略ノートでは、料金情報が複雑になった段階で pricing_master.json に集約し、そこから表示と計算を組み立てる構成に変更しました。
小さなコンテンツサイトでも、情報量が増えればデータ管理の設計が必要になる。
実際に運営してみて、それをかなり実感した改善でした。