とある休日
娘(5歳)「パパ、一緒に技術ブログを始めない?」
娘「昔から私とパパがローカルに書き溜めてる技術記事が、かなり溜まってきたでしょ?」
娘「それをブログとして公開してみようよ」
ワイ「おお、それは楽しそうやな」
ワイ「どこのブログサービスを使おうかいな」
娘「せっかくなら、ブログシステムから自分たちで作ってみようよ」
娘「私はフロントエンドを担当するから」
娘「パパはRuby on Railsか何かで、APIを作ってよ」
ワイ「おお、Ruby on Railsなら昔やったことあるわ」
ワイ「RailsのAPIモードで、ブログ記事を返すエンドポイントを作ればええんやな」
娘「そうそう」
次の日
ワイ「API、できたで!」
ワイ「/api/articles
っていうエンドポイントにアクセスしてくれたら」
ワイ「ブログ記事が取得できるようにしておいたわ」
娘「さすがパパ」
娘「そういえば、10記事ごとにページを分けたいんだけど」
娘「どうしたらいいかな?」
ワイ「おお、ページネーションいうやつやな」
ワイ「/api/articles?page=2
って感じでAPIにアクセスしてくれたら」
ワイ「2ページ目を取得できるようになってるで!」
娘「わ〜、気が利いてるね!」
ワイ「ゲヘヘ」
また次の日
娘「フロントのコーディングもだいぶ進んだよ!」
娘「パパ、見てみて!」
娘「これがTOPページだよ!」

ワイ「おお、バッチリやん」
ワイ「ナイスTOPページや」
娘「それでパパ、相談があるんだけど」
ワイ「なんや、娘ちゃん」
娘「あのね、TOPページはこれでいいんだけど・・・」
閲覧者「やめ太郎の記事は読みたくない!」
閲覧者「娘ちゃんの記事だけ読ませてくれや!」
娘「↑こんな閲覧者さんのニーズも、きっとあるじゃん?」
ワイ「せやな」
娘「だから、私の記事だけを表示するページも作りたいんだけど」
娘「その場合は、どうAPIから取得すればいいの?」
ワイ「うーん、そんな方法は用意してないし、今後実現できる気もせんわ」
ワイ「・・・せや!」
ワイ「フロント側でフィルタリングして、ワイの記事だけ消してくれや」
ワイ「それで『娘ちゃんの記事だけのページ』ってことになるやろ」
娘「いや、それは駄目でしょ」
娘「そこはバックエンド側でフィルタリングしないと」
ワイ「なんでや、フロント側でもフィルターできるやろ」
ワイ「JavaScriptの配列のfilter()
メソッドとかで」
娘「うーん、駄目だと思う」
なぜ駄目なのか
娘「まず、この技術ブログは」
娘「1ページごとに10件の記事を表示するじゃん?」
ワイ「せやな」
娘「つまり、10記事ぶんのデータをAPIから取得するわけ」
ワイ「そらせや」
娘「そこから、フロント側でフィルターして」
娘「パパの記事だけを取り除いちゃうと」
娘「画面上はこんな感じになっちゃうの」

ワイ「ああ、7件になってもうたな」
娘「この表示の仕方だと、閲覧者さんに誤解を与えてしまう可能性があるの」
ワイ「そうなん?」
娘「そうだよ」
娘「まず閲覧者さんがTOPページを見て」
閲覧者「このブログは、1ページに10件表示するんやな」
娘「ってことを覚えたとして」
娘「次に、私の記事だけのページ」
娘「つまり7件しか表示されていないページを見た場合に・・・」
閲覧者「娘ちゃんの記事は、まだ7件だけか」
閲覧者「ほな、2ページ目は無いんやな」
娘「って誤解しちゃうかもしれないじゃん」
娘「ちゃんとページネーション部分を見れば、5ページ目まであることは分かるんだけど」
娘「パッと見で『7件だけか』って印象を与えるでしょ?」
娘「または逆に・・・」
閲覧者「このページは、1ページに7件表示するんやな」
閲覧者「TOPページは1ページに10件なのに」
閲覧者「このページは中途半端な数で区切って、変なサイト!」
娘「っていう誤解を与えちゃうかもしれないし」
閲覧者「あれ、1ページ目は7件、2ページ目は9件、3ページ目は10件表示されたで」
閲覧者「ページごとに表示件数ガタガタやん」
娘「ってことも起こりそうだよ?」
ワイ「ま、まあな」
ワイ「で、でもまあ、そこまでクリティカルではないやろ」
娘「それに、もしも1ページ目の10件がたまたま全部パパの記事だった場合は」
娘「こんなことになっちゃうよ?」

ワイ「Oh...」
ワイ「何も無いのに、ページネーションだけある・・・」
娘「ね、変でしょ?」
娘「だから、フロント側でデータのフィルターなんかしちゃ駄目」
ワイ「わ、分かったわ・・・」
次の日
ワイ「よっしゃ!」
ワイ「ワイの記事と娘ちゃんの記事を別々に取得できるエンドポイントも作成したで!」
ワイ「娘ちゃんの記事だけを取得するためのエンドポイントは」
ワイ「/api/articles/musume
や!」
娘「ありがとう、パパ!」
娘「あれ、でもTOPページがエラーになってるよ?」
ワイ「おお、実はな」
ワイ「APIを改修したときに」
ワイ「元のエンドポイントを消してしもうたんや」
ワイ「パパと娘ちゃんの記事を一緒に取得できる・・・」
ワイ「最初に作った/api/articles
っていうエンドポイントな」
娘「え・・・消しちゃったの?」
ワイ「でも大丈夫や」
ワイ「パパの記事だけを取得するための/api/articles/papa
っていうエンドポイントと」
ワイ「娘ちゃんの記事だけを取得するための/api/articles/musume
っていうエンドポイント」
ワイ「その2つがあるからな」
ワイ「2つのエンドポイントから5件ずつ取得して、マージして表示すれば」
ワイ「同じことやろ?」
娘「いやいや、ぜんぜん駄目だよ・・・」
なぜ駄目なのか
娘「例えば、私の記事が合計20件あって」
娘「パパの記事が合計1件だけだった場合はどうなると思う?」
ワイ「ええと、娘ちゃんの記事用のエンドポイントから5件取得して」
ワイ「ワイの記事用のエンドポイントから1件取得するから」
ワイ「こんな感じになるな」

ワイ「あ、また10件未満になってもうた・・・」
娘「今回の例だと、私とパパの記事を合わせて合計21件だから」
娘「本来は3ページ目まであるはずなのに」
娘「パッと見だと10件未満しかないから」
閲覧者「このブログの記事は、まだ6件だけか」
閲覧者「ほな、2ページ目は無い感じか・・・?」
娘「もしくは」
閲覧者「このブログは6件ごとにページネーションすんねやな」
閲覧者「キリ悪っ。アホちゃうか」
娘「って思わせちゃうでしょ?」
ワイ「た、確かに・・・」
娘「だから、フロント側でのマージはオススメしない」
ワイ「そうなんかぁ・・・」
娘「それにね」
娘「2つのエンドポイントから記事を取得して、フロント側でマージすると」
娘「1ページ目と2ページ目で、記事の順番もおかしくなっちゃうの」
ワイ「そうなん・・・?」
ワイ「ちゃんとソートもすれば大丈夫ちゃう?」
娘「じゃあ、それも説明するから見ててね」
娘「さっきマージした状態で、一番新しい記事は・・・」

娘「↑この、一番下の記事。」
娘「パパが2018/07/21
に書いた、」
娘「変数って何か分からないから後輩に聞いてみた、って記事でしょ?」
ワイ「せやな」
ワイ「古い順に並んどるからな」
娘「じゃあ、2ページ目以降にはいつ頃の記事が表示されると思う?」
ワイ「いつ頃って・・・」
ワイ「2018/07/21
以降の記事が表示されるやろ」
ワイ「古い順なんやから」
娘「普通はそうだよね」
娘「閲覧者さんも・・・」
閲覧者「このページは、古い順に記事を表示してるんやな」
閲覧者「そして、1ページ目の1番下の記事は2018/07/21
」
閲覧者「じゃあ、2ページ目はそれより新しい記事が表示されるんやろな」
娘「って考えるわけ」
ワイ「せやな」
娘「でも、実際に表示されるページは・・・」

娘「↑こうなっちゃうの」
娘「一番上の記事を見て?」
娘「2018/07/21
より過去である2018/07/16
の記事が表示されてるでしょ?」
娘「2ページ目は未来のはずなのに」
ワイ「ほんまや・・・」
ワイ「フロント側でマージした後にソートするやり方だと」
ワイ「ページネーションが絡んだ場合に」
ワイ「ほんまの順番通りにはならへんのやな・・・」
娘「そういうことだね」
ワイ「ごめんな娘ちゃん」
ワイ「元々あったエンドポイント、つまりワイと娘ちゃんの記事を一緒に取得できるやつ」
ワイ「もう一回ちゃんと作り直すわ・・・」
次の日
ワイ「よっしゃ、できたで!」
ワイ「ワイと娘ちゃんの記事を一緒に取得できるエンドポイントも復活や!」
ワイ「これで完成やな!」
娘「パパ、待って」

娘「さっきも思ったんだけど」
娘「記事が古い順に並んでるよね?」
娘「普通は新しい順じゃない?」
ワイ「ついにその事に触れてもうたか・・・」
娘「新しい順でソートされた状態で記事を取得するには」
娘「どうすればいいの?」
ワイ「そ、それは・・・分からん・・・」
娘「・・・」
ワイ「フロント側で、新しい順にソートしてくれや・・・」
娘「だから、駄目だって」
ワイ「それも駄目なん・・・?」
なぜ駄目なのか
娘「サーバ側で古い順にソートされてるってことは」
娘「APIから一番古い10件の記事が返ってきてるってことじゃん?」
ワイ「せやな」
娘「それを、フロント側で新しい順にソートすると・・・」

娘「↑こんな感じになるの」
ワイ「ええやん、新しい順に表示されとるやん」
娘「そうだね」
娘「新しい順に表示されてるよね」
娘「一番古い10件がね」
ワイ「せや」
ワイ「問題ないやろ」
娘「いや大ありでしょ」
娘「すごく昔の記事が最新の記事みたくなっちゃってることが問題なの」
ワイ「どういうこと?」
娘「この画面を見た閲覧者さんは・・・」
閲覧者「ふむふむ、このブログは新しい順に記事を表示しとるな」
閲覧者「なになに、最新の記事の投稿日は・・・2018年7月!?」
閲覧者「2年間も更新されとらんやんけ!」
閲覧者「技術ブログとして終わっとるな・・・」
娘「って思われちゃうかもしれないよ?」
娘「実際には、今月書いた記事とかも沢山あるのに」
ワイ「Oh...」
ワイ「それはアカンわ・・・」
まとめ
- APIから取得したデータをフロント側でフィルタ・マージ・ソートなどすると、色々と矛盾が生じる。
娘「だから、そういうのはアンチパターンなの」
娘「こんなの常識だよ?」
娘「パパ、幼稚園からやり直したら?」
ワイ「いや幼稚園でAPIのアンチパターン教えてくれるんかい!」
〜おしまい〜