スタジオ予約管理システムを作りました

はじめに
今回は、音楽スタジオやレンタルスペース向けに、スタジオ予約管理システムを自作したので、その経緯や実装内容について紹介します。
GitHubリポジトリ及び資料
GitHubリポジトリ
https://github.com/acaibawl/studio_reservation_management
設計資料
https://drive.google.com/drive/folders/1uFC7cJku7mLBT1R4KtVR8O4pWcHdH8rI
NotebookLMによる設計概要ラジオ
https://notebooklm.google.com/notebook/433f9a87-58fe-40e9-837c-8c94f516f5eb/audio
システム概要
このシステムは、主に会員がスタジオの予約状況を確認して予約を行う機能と、オーナーがスタジオ、予約、営業時間・定休日、臨時休業日、会員などを管理する機能から構成されています。
主要な会員とオーナーそれぞれの機能概要
会員
- 会員登録
- メールアドレスの認証コードを使った認証を経て、氏名、住所、電話番号、パスワードなどの情報を登録します
- 会員ログイン
- メールアドレスとパスワードでログインします
- スタジオ予約
- 指定した日付のスタジオの予約空き状況を確認できます
- 空いている時間に対してスタジオ予約の申し込みができます。予約時には1時間から6時間まで利用時間を選択できます。予約申し込みの前に、選択した時間と利用時間に対して、予約可否が再度判定されます
- 自分の予約済み確認
- ログイン中の会員が予約済みの内容を確認できます。現在時刻よりも後の予約のみ表示されます。
- 会員情報修正
- 自分の登録情報(メールアドレス、氏名、住所、電話番号)を確認・修正できます。メールアドレスの修正には認証コードが必要です
- パスワード再設定
- パスワードを忘れた場合、メールアドレスに送信されるトークンを使用してパスワードを再設定できます
オーナー
- オーナーログイン
- オーナー用のメールアドレスとパスワードでログインします
- スタジオ管理
- スタジオ(Aスタ、Bスタなど部屋)の一覧表示、 登録、修正、削除ができます
- スタジオは名前と開始時間(入れ替え時間)を持ちます
- 将来の予約が入っているスタジオは削除できません。将来の予約がある場合、スタジオの開始時間の変更はできません
- 営業時間・定休日管理
- スタジオ全体の営業時間(開始時間、終了時間)と定休日(日~土)を設定できます
- これらの設定は予約可否判定に利用されます
- 臨時休業日管理
- 特定の日付を臨時休業日として追加・削除できます
- 臨時休業日も予約可否判定で予約不可として扱われます
- 予約管理
- 指定した日付の全スタジオの予約状況を、予約不可(x)、予約済み(✅)、予約空き(O)の3つのステータスで一覧表示できます
- 予約済みの詳細を確認できます。詳細には会員情報や予約時のメモが含まれます
- 電話での予約問い合わせを想定し、オーナーも手動で予約の登録ができます。
- 利用時間やメモを指定して登録します。この際、会員IDは固定値(9999999)が使用されます
- 既存の予約の修正(利用時間、メモ)や取消ができます。修正時には利用時間に対する予約可否判定が再度行われます
- 会員管理
- 会員の一覧を表示し、名前での部分一致検索やページネーションに対応しています
- 会員の詳細(氏名、メールアドレス、住所、電話番号)と、その会員の現在時刻より後の予約一覧を確認できます
予約可否判定
スタジオ予約時やオーナーによる予約管理(一覧表示、修正、登録)において、予約が可能かどうかは以下の基準に基づいて判定されます
- 既に他の予約が入っていないか
- 現在時刻より前ではないか
- 現在日付より2ヶ月以上先の日付ではないか
- 適用営業日(予約開始時間の属する日付。営業時間が日を跨ぐ場合は判定ロジックが変わる)がスタジオの定休日ではないか
- 適用営業日が臨時休業日ではないか
- 営業時間外ではないか
システムの解説動画
作成したスタジオ予約管理システムの会員側機能について説明する動画を録りました pic.twitter.com/rpgyYK5vIV
— 浅井 (@acai_it_life) June 7, 2025
スタジオ予約管理システムのオーナー側の機能についても説明動画を録りました pic.twitter.com/4ehCMhoM5A
— 浅井 (@acai_it_life) June 7, 2025
DBのテーブル構成
- members: 会員情報
- owners: オーナー情報
- studios: スタジオ情報
- reservations: 予約情報
- regular_holidays: 定休日
- business_times: 営業時間
- temporary_closing_days: 臨時休業日
使用した技術スタック
バックエンド
- PHP 8.4
- Laravel 11.31
- tymon/jwt-auth 2.2 (バックエンドのjwt認証)
フロントエンド
- node v22.9.0
- npm 10.8.3
- Vue 3.5.13
- Nuxt 3.17.2
- vuetify-nuxt-module 0.18.6
- Pinia 3.0.2
- VeeValidate 4.15.0
- yup 1.6.1
ミドルウェア・インフラ
- Docker 20.10.12
- Docker Compose v2.2.3
- nginx
- mysql 8.0.37
- redis 8.0.1
- mailpit v1.25.0
開発の流れ
システムの要件整理
- ユースケース図
- 画面イメージ
- 詳細設計・DB設計
実装
- バックエンド(ユニットテストも並行して作成)
- 予約可否判定の箇所のみ実装前に網羅が必要なテストケースを作成
- フロントエンド(一部バックエンドを手直ししつつ作成)
苦労した点・工夫した点
会員側とオーナー側でそれぞれ認証できる、いわゆるマルチログインと呼ばれる機能を初めて実装しました。
認証周りを作るのは不安があったのですが、Laravelの認証機能の柔軟な作りの素晴らしさを知ることができました。
他には、あまりないことだとは思いますがスタジオ数が大量に増えた時にスマホで表示するとテーブルが見切れてしまう事象が発生しました。
これには、横スクロールするスタイルを当ててスマホでも問題なく表示できるようにしました。
失敗・次回に活かしたい点
このシステムでは予約可否判定ロジックによって重複した予約を避ける作りにはなっているのですが、ほぼ同時に予約が入った場合に、3時間と6時間など予約時間が異なると双方の予約ができてしまうことが後からわかりました。
最初の設計時に予約の時間枠を1時間ごとにDBのレコードにするか迷って、そうしない判断をしたのですが、そうするべきでした。
そうすれば予約可否判定のロジックももっとわかりやすいものになったはずなのですが、設計当初にロジックが難しくなりそうだけどレコードをたくさん持たない方が性能が良くなりそうなのと、難しいロジックに挑戦してみたいという思いでこちらの手法を取ってしまったのですが、判断を誤っていました。
以前参画したECサイトの案件で、商品の在庫数を在庫数カラムではなく在庫数テーブルで1在庫ごとにレコードを持つ仕様になっていたのはこういうことを避ける為だったんだなと身に沁みて理解しました。
DBレベルで誤ったデータの処理を防げるテーブル設計にする必要があったんですね。
まとめ
要件整理を5/4に始めて、今日が6/8なので1ヶ月強で作成できたことになります。
バックエンドもフロントエンドもオーナー側から着手し、続いて会員側を作成しました。
オーナー側のフロントエンドができた6〜7割完成したあたりで、もうこの辺でいいんじゃないかという気持ちになったのですが、最後まで完走できて良かったです。
これでLaravelとNuxt3、それにVuetify3とVeeValidate&yupの基本的な扱い方は理解できたかなと思います。
最後に失敗も露呈してしまいましたが、テーブル・データ設計大事!!ということが身に沁みてわかったので、とてもよい学びになりました。
追記
予約が重複してできてしまうという事象ですが、こちらはテーブルロックをかけることで回避することができました。
Laravelの持つ通常のトランザクションメソッドでトランザクション管理するほうが圧倒的に管理しやすいですし、先述のDB設計のほうがパフォーマンスへの影響も小さいのでミスったなという後悔は拭えないですが、それでもやれないよりは対処できてよかったです。
https://github.com/acaibawl/studio_reservation_management/pull/91/files