SpringBoot と Vue.js を採用した水族館予約システムのアーキテクチャ解説

システム概要と技術選定

本プロジェクトは、水族館の施設予約管理を目的とした Web アプリケーションです。バックエンドにはスプリントフレームワークを採用し、フロントエンドではモダンな JavaScript ライブラリを使用することで、効率的な開発と柔軟なユーザーインターフェースを実現しています。

主要な技術スタック

バックエンド:Spring Boot

Spring Boot は、Spring エコシステムの複雑な設定を抽象化し、スタンドアロン型のアプリケーションを迅速に立ち上げることを可能にするフレームワークです。内部に Tomcat などのサバートを内蔵しており、WAR ファイルとして外部サーバーへ展開する必要がなく、JAR フォーマットでそのまま実行可能です。自動設定機能により、クラスパス上の依存関係に基づいて環境が自動的に構成されるため、開発者はビジネスロジックの実装に集中できます。また、Spring Data や Spring Security といったエコシステムとの親和性も高く、拡張性の高い基盤を提供します。

package com.aquarium.reservation;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class ReservationCore {

    public static void main(String[] args) {
        SpringApplication.run(ReservationCore.class, args);
    }

    @GetMapping("/api/health")
    public String checkStatus() {
        return "System Status: Operational";
    }
}

上記のコードは、Spring Boot のエントリーポイントを示しています。ReservationCore クラスにおいて、@SpringBootApplication アノテーションがコンポーネントスキャンと自動設定をトリガーし、RestController によって RESTful API の提供が可能になります。/api/healthエンドポイントへのアクセスにより、サーバーの稼働状況を確認する仕組みとなっています。

フロントエンド:Vue.js

Vue.js は、宣言的なレンダリングと仮想 DOM を活用して、UI 状態の更新を効率化する JavaScript フレームワークです。双方向データバインディングにより、モデルの状態変更が即座にビューに反映されます。この特徴を活かし、動的な予約フォームやリアルタイムな空き状況表示などの実装に適しています。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Aquarium Booking Demo</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <p>現在の利用人数:{{ visitorCount }}人</p>
    <button @click="addVisitor">参加者を追加</button>
  </div>

  <script>
    const app = new Vue({
      el: '#app',
      data: {
        visitorCount: 0
      },
      methods: {
        addVisitor: function() {
          this.visitorCount += 1;
        }
      }
    });
  </script>
</body>
</html>

このサンプルでは、Vue インスタンスが #app セレクターにマウントされ、visitorCount というデータプロパティが定義されています。ボタンクリック時に実行されるメソッドがデータを更新すると、DOM が自動的に再描画され、画面に表示される数値が変化します。

永続層:MyBatis

MyBatis は、Java オブジェクトと SQL ステートメントを紐付ける ORM(Object Relational Mapping)ライブラリの一種です。XML マッパーまたはアノテーションを用いて SQL を記述することで、データベース操作の可視性を高めます。これにより、SQL のチューニングを行う余地を残しつつ、型安全なデータアクセスレイヤーを構築することが可能です。

セキュリティと認証ロジック

システムの安定運用のため、ユーザー認証および認可制御を実装しています。セッショントークンを利用し、API アクセス権限を管理する構造を採用しました。

// トークン生成サービスの実装例
@Service
public class SessionManager {
    
    @Autowired
    private TokenMapper tokenMapper;

    public String createSessionToken(Long userId, String username, String role) {
        // 既存トークンの確認
        TokenData existingToken = tokenMapper.findByUser(userId);
        
        // ランダムな識別子の生成
        String sessionKey = RandomUtil.generateSecureId(32);
        
        // 有効期限の設定(60分)
        LocalDateTime expireTime = LocalDateTime.now().plusMinutes(60);
        
        if (existingToken != null) {
            tokenMapper.updateToken(existingToken.getId(), sessionKey, expireTime);
        } else {
            tokenMapper.insertNewToken(userId, username, role, sessionKey, expireTime);
        }
        
        return sessionKey;
    }
}
// リクエストフィルタの実装
@Component
public class AccessControlFilter implements HandlerInterceptor {

    private static final String HEADER_AUTH_KEY = "X-Auth-Token";

    @Autowired
    private SessionManager sessionManager;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // CORS ヘッダーの設定
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");

        // OPTIONS リクエストの場合は即座に完了
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            return true;
        }

        // 認証をスキップするアノテーションの確認
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            if (method.hasMethodAnnotation(IgnoreSecurity.class)) {
                return true;
            }
        }

        // リクエストヘッダからトークンを取得
        String authHeader = request.getHeader(HEADER_AUTH_KEY);
        if (StringUtils.isEmpty(authHeader)) {
            sendUnauthorizedResponse(response);
            return false;
        }

        // トークン検証
        TokenData validToken = sessionManager.validateToken(authHeader);
        if (validToken == null || validToken.isExpired()) {
            sendUnauthorizedResponse(response);
            return false;
        }

        // ユーザー情報をセッションに格納
        request.getSession().setAttribute("USER_ID", validToken.getUserId());
        request.getSession().setAttribute("USER_ROLE", validToken.getRole());
        
        return true;
    }

    private void sendUnauthorizedResponse(HttpServletResponse response) {
        try {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\": 401, \"msg\": \"Unauthorized\"}");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上記のフィルターは、HTTP リクエストが処理される前に介在し、トークンの有効性を検証します。無効なトークンや欠落している場合は 401 エラーを返却し、有効な場合は要求されたハンドラメソッドへの処理を継続させます。

データベーススキーマ設計

予約データの保管には以下のようなテーブル構造を想定しています。

-- ----------------------------
-- Table structure for aquarium_booking
-- ----------------------------
DROP TABLE IF EXISTS `aquarium_booking`;
CREATE TABLE `aquarium_booking` (
  `book_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '予約 ID',
  `member_name` varchar(50) NOT NULL COMMENT '利用者名',
  `visit_date` date NOT NULL COMMENT '訪問予定日',
  `ticket_count` int(11) DEFAULT '1' COMMENT 'チケット枚数',
  `status` tinyint(4) DEFAULT '0' COMMENT 'ステータス:0=未確認,1=確定',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '作成日時',
  PRIMARY KEY (`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='水族館予約テーブル';

このテーブルには、予約者の基本情報、訪問日程、枚数が記録されます。ステータス管理により、キャンセルや確定処理などの業務フローを制御できます。

システム品質保証

テスト戦略の目的

開発ライフサイクルの最終段階において、要件仕様に合致するかを検証するプロセスが必要です。ユーザー視点からの動作確認を通じて、潜在する不具合を特定し、修正を加えることで信頼性の高いプラットフォームを構築することを目指します。

機能テストケース

ブラックボックステスト手法を採用し、入力値と期待される出力を対照させながら機能を評価します。

ログイン認証機能のテスト例:

入力条件 期待動作 実際の結果 判定
ID:admin / PW:pass123 / CAPTCHA:正 管理者画面へ遷移 正常に遷移した 合格
ID:admin / PW:wrong123 / CAPTCHA:正 認証エラー表示 パスワードエラーが出た 合格
ID:空 / PW:pass123 / CAPTCHA:正 必須項目エラー ID 未入力警告 合格

予約登録機能のテスト例:

入力データ 期待結果 実際の結果 判定
氏名:山田 / 日付:2023-11-01 / 枚数:2 登録成功メッセージ データが保存されリストに反映 合格
氏名:空白 / 日付:2023-11-01 / 枚数:2 登録エラー 必須入力チェックエラー 合格
氏名:鈴木 / 日付:2023-11-01 / 枚数:0 登録エラー 枚数エラー通知 合格

検証結論

実施したテスト結果より、システムの主要機能は仕様通りに動作することが確認されました。特に、入力補完やエラーハンドリングの部分は、ユーザーにとって使いやすい挙動を示しています。今後のメンテナンスにおいては、ユーザビリティの更なる向上を図りつつ、パフォーマンス最適化の観点での改善を継続的に行う予定です。

タグ: SpringBoot vuejs MyBatis Java web-security

5月15日 05:51 投稿