DBからデータを集計し、新たなデータを生んで保存するようなことをしたくないと思っている。
サンプルケース
家計簿システムがある。
お金の出入りがあったらその日に金額を入力し保存する。
月集計と年収計が確認できる。
[実装]
- 金額入力をしたらdailyテーブルにレコードが作成される。
- 毎月1日に先月分の月間集計処理が実行されmonthlyテーブルにレコードが作成される。
- 毎年1月1日に去年の年間集計処理が実行されyearlyテーブルにレコードが作成される。
つまり、定期的にテーブルデータを集計したものを別テーブルに保存するような作りになっている。
データ不整合が発生しやすいからしたくない
たとえば、去年の11月に払った飲み会の支払いを今年の1月に思い出したので入力したいが、過去入力できるように作ってなかったのでdailyテーブルに直接データを入れた。
しかし確認すると去年の11月の集計金額と去年の年間集計金額が、今入れた金額を考慮した集計金額になっていない。
なぜなら、これらのデータはすでに集計処理が終わっているからだ。
なので、去年11月の月間集計処理を再実行した後、去年の年間集計処理を再実行する必要がある。
実行する順番を間違ってもいけないし、どれかをやり忘れてもいけない。
これはイメージしやすい簡単な例だけど、一般的なシステムではもっと複雑なルールで行われている場合が多いので、再実行すべき処理を把握したり、再実行して大丈夫なのか調べる等しなくてはいけなくてめちゃめちゃ大変になる。
導出できるデータは保存しない
導出元と導出先の2つのデータは本質的には同じものなので、一方が変更されると不整合が起こる。
だから保存しない。
必要になったときに毎回導出するようにする。
毎回計算させると遅い?
まずはindexを使って必要な速度が出ないかを試す。
それでも駄目なくらい大規模な集計をしないといけないなら、次にキャッシュを検討する。
大事なのは、このキャッシュはいつでも再生成可能にしておくこと。
消えてしまっても、速度以外の問題は発生しないようにしておく。
自動的に行われるようにフックとかを仕掛けておけば良い?
「月データに変更が入ったら自動的に集計処理が行われるようにしておけばいいのでは?」
という意見もあると思うけど、個人的にはNG。
自動処理をアプリケーションコード側でやると、DBを直接操作でデータを入れたときに対応できない。
DBのフックでやると、ビジネスロジックがDBにまで食い込んだ感じになってよくない。
(コードで追いかけると、どこでなぜ集計がおこなわれているのかがわかりにくい。)
「自動的に実行される」というのは一見便利な感じもするけど、意図せず実行される処理が存在するというのは良くないパターンが多い気がしている。