その新規登録、適切な設計になっていますか?

はじめに

エス・エム・エスのヘルスケア事業部所属のエンジニアの今村と申します。この会社に転職してきて気がつけば10年以上となります。

その間に、新規登録の処理を何度も何度も設計してきました。設計とは言うものの、入力用の画面を用意しPOSTしたらその値をサーバーサイドで検証してDBに登録するという大枠の流れは同じです。しかし最近になって、それで本当に良いのか? と再考する機会がありました。

今回は、その時に再考した内容を整理したいと思います。

再考することになった経緯

仕事では、webサービスの改善・運用の他、業務で使うkintoneのカスタマイズも担当しています。

ある日、kintoneアプリのカスタマイズ要望がありました。具体的には、画面内の項目Aに特定の値を入力して保存した場合、ダイアログを表示して「次に担当者がとるべきアクションを明示したい」というものです。

kintoneには固有のイベントハンドラがあり、データの保存が成功したことをトリガーにして処理を実行することができます。今回はこれを使えば簡単に実装できると思っていました。

前提として、今回の値は新規登録と更新のどちらのケースでも入力されうるものです。そして、kintoneの画面は、新規登録を行っても完了ページには遷移せず、その画面内の項目が全てdisabledになるのみです。もしそこで「編集ボタン」をクリックすると、disabledが解除され、各項目の値を変更したりできるのです。業務アプリケーションではkintoneに限らず、よくある仕様と言えるでしょう。

しかしこの仕様、合理的なように見えますが、ユースケース次第では厄介なことになります。

更新画面ではURLで対象レコードのIDを指し示している必要があるものの、新規登録画面入力時には、まだIDが払い出されていません。このためkintoneでは、登録完了のレスポンスをAPIから受け取ったあと、フロントエンド側でリダイレクトをかけて編集画面を表示していました。

その結果、新規登録時の送信成功イベントハンドラを使って表示したダイアログは、一瞬だけ表示されるのみで、すぐリダイレクトされることがわかりました。これではイチロー並みに動体視力が発達した人しかメッセージを読み取ることができません。

結局、新規登録時には処理成功のイベントハンドラでCookieを発行し、編集画面が表示されたタイミングでCookieの有無をチェックしてダイアログを表示し、その後にCookieを削除するという煩雑な処理が必要でした。

そこで、ふと思ったのです。「新規登録では、入力完了後にデータを保存し、IDを払い出すのが唯一の正解なのか?」と。

新規登録という名の更新

気になった私は、早速世の中のさまざまなwebサービスはどうなっているのか調査しました。結果、一部のサービスでは、新規登録を行うと入力開始前にIDが払い出されて、新規登録画面という名の更新画面を表示していることがわかりました。

メジャーなサービスでは、Google Workspace(Docs、Sheets)、note、Zenn.devがこのような設計になっています。これらのサービスでは、一度登録画面を表示した後に入力を行わず離脱すると、そのデータは一覧上には表示されません。しかしURLを直接入力すれば表示できます。数日経つと、URLを入力してもアクセスできなくなります。以降ではこの方式を便宜的に「登録編集共通方式」と呼ぶことにします。

登録編集共通方式のメリットとデメリット

この処理方式、主にフロントエンド観点では、実装が楽になるというメリットばかりとなります。一方バックエンドは、フロントエンドの実装が楽になったしわ寄せとして、主にDB周りの設計に複雑さが生じます。以下詳しく整理してみます。

メリット

フロントエンド側の考慮事項が減る

登録と編集を共通化してしまえば、新規登録を編集と同等の挙動に見せかけるための様々な工夫が不要になります。当然、実装もシンプルになると言えます。

多重POST問題から解放される

IDが払い出されていない場合、同一の投稿がすでに送信されていないか判断する必要があります。フロント側でボタンをごく短時間不活性化すれば良いとの意見もありますが、ユーザーは数秒後に同じ内容を送信してくることもあり、バックエンド側での多重POST判定の処理が必要なケースもあります。

他方、IDを払い出す場合、内容如何によらず、POSTされたら素直に上書き保存すれば良いだけなので、先述のような考慮は不要になります。

デメリット

テーブル設計が複雑になる

この設計を実行に移すには、未入力の状態でも登録ができるように、多くのカラムでnullを許容する設計にするか、IDを払い出すための最小項目の親テーブルと実データを格納する子テーブルの関係で設計する必要があります。

チームの方針として、テーブルの項目に原則nullを使用しないことになっている場合は、自ずと後者の設計を選択することになるでしょう。

新規登録時点では親テーブルのレコードのみを登録してIDを払い出し、最初のデータ保存のタイミングで子テーブルのレコードを登録します。これはアプリケーションの都合にDB設計が従属する形となるため、受け入れられないこともあるでしょうし、受け入れられたとしても、テーブルの管理が従来より煩雑になるのは間違いありません。

ごみレコードが沢山できる/定期的に削除処理が必要になる

新規登録の画面を開く都度にレコードが作成されるため、結局その後保存されずに離脱されてしまうと、そのレコードは、ゴミとして蓄積していくことになります。

DBの性能とシステムの規模によっては、パフォーマンスの劣化を早めてしまうため、定期的な削除が必要で、その場合バッチ処理を別途作成することになるでしょう。

まとめ

以上の通り、 登録編集共通方式について、採用した場合の具体的なメリットとデメリットを整理しました。

繰り返しになりますが、登録編集共通方式ではフロントエンド周りの開発コストが削減できそうですが、バックエンド周りの開発コストは増える可能性がありそうです。

例えばフロントエンド周りの機能が今後複雑化していきそうな場合には、バックエンド側が開発コストを多く負い、フロントエンド側をシンプルに保てる登録編集共通方式のほうが長期的に優れた結果をもたらすでしょう。一方、従来の会員登録のような機能では登録編集共通方式の採用はデメリットが目立つ結果になりかねません。

このため、プロジェクト全体の方針やユースケースに応じて、既存の方式と登録編集共通方式を比較検討しながら、その時々でベターな選択をしていけると良いかなと思います。