最近アプリケーションの設計するときに気をつけてること

最近、機能の設計をレビューしたり話し合ったりするときに、うん・・・?ってなることがあって、でも駄目な理由をうまく説明できなかったのでちゃんと言語化してみる。ちなみにRailsの話です。

DBには事実だけを記録するようにする

色々開発してきて思ったのは、テーブルに状態を持たせるようにするとあっという間に複雑度が増してしまうということ。 status カラムや hoge_flag みたいなカラムを持つこと自体は全然否定しないけど、本当にそれが最適なのかは慎重に考えた方がよいと思う。

これは例ですけど、会員申し込み => 何か審査 => 会員登録完了 みたいなフローのアプリケーションを作る時に、会員情報のテーブルを1つだけ作りstatusapproved_at みたいなカラムを突っ込んで、申し込み状態と申込み完了状態を アプリ側で頑張って判定しようとしている例はまれによくある。

こうすると何が辛いかというと、他のレコードから関連を辿って参照しようとしたときに常にレコードのカラムの状態を気にしないといけなくなってしまう。それに申し込み時の情報と、会員登録後に必要になる情報は必ずしも一致しないはずなので、作ったときは同じだとしても後々拡張するときに辛くなってくる。また、会員情報にDB上のユニーク制約をかけたくなっても掛けられないのでアプリケーションでかけるしかなくなる。(そしてアプリケーションで掛けた制約は結構簡単にすり抜ける…)

こういうときは事実が何なのかに着目すると、きれいに設計ができると思う。今回でいうと申込み というテーブルと 会員情報 というテーブル二つに分けてしまう。そうすれば上記にあげたような懸念はスッキリ解決する。 同じようなカラムが二つのテーブルに別れることを懸念するかもしれないけど、本来違うものを同じテーブルに押し込むことのデメリットの方が大きい。

同じような理由で論理削除もよく考えたほうがよいと最近は思っている。削除という状態を追加するよりも、素直にそのテーブルからデータは消してしまい、必要な情報だけ履歴的に別のテーブルにコピーしておいた方がアプリケーションをシンプルに保つ事ができるとおもう。

安易にHashを作らない

Rubyはダックタイピングなのでそんなに厳密にやってもしょうがない」みたいな話は一定理解するし、そういうゆるさがRubyのいいところだと思いつつも、巨大なHashを作ってそれを色んな所に引き回したりするのは結構最悪だと思うようになった。Hashに何が入っているか、何のkeyを持つべきなのか実装を全部追わないと分からないからだ。

そしてHashが巨大化していくると、そのHashを操作するための実装がそのHashを引き回す側に出てきたりしてあっという間に良くない感じになっていく。

何かを作る場合も、ハッシュではなく、クラスを作ってそこに attr_accessor を定義したほうがよいと思う。そうしておけば、クラスを見ればどんな値を持つべきなのかすぐに分かる。それに、値の中身を書き換えたり、デフォルト値を与えたり、変換したりといった処理をそのクラスに押し込めていくこともできる。

まとめ

最近考えたことをまとめてみた。定期的にこういうの考えて吐き出しておきたい。