Skip to content

eugenekweb/java-CW-SpringFramework

Repository files navigation

Ковалёв Евгений.

Учебный проект по курсу "Фреймворк Spring и работа с REST API".

МИФИ, магистратура ИИКС, 2 курс


Hotel Booking System

Проект представляет собой разработку REST API для системы бронирования отелей на базе Spring Boot с использованием микросервисной архитектуры.

Цель — создать распределённое приложение с несколькими сервисами, реализующими основные функции бронирования, управления гостиницами и маршрутизации запросов через API Gateway.

Архитектура

Система состоит из 5 модулей:

  • shared - Общие DTO, exceptions, IdempotencyRecord, API константы
  • eureka-server (порт 8761) - Service Registry для динамического обнаружения сервисов
  • gateway (порт 8080) - API Gateway с JWT аутентификацией и трассировкой запросов
  • hotel-service (порт 8082) - Управление отелями и номерами
  • booking-service (порт 8083) - Управление бронированиями и пользователями

Архитектурная диаграмма

graph TB
    %% External Layer
    Client["`**Клиент**<br/> / Web Browser`"]

    %% Gateway Layer
    subgraph Gateway["`**API Gateway Layer**<br/>:8080`"]
        GW["`**API Gateway**<br/>Spring Cloud Gateway`"]
        CF1["`**CorrelationIdFilter**<br/>X-Correlation-Id`"]
        CF2["`**JwtAuthenticationFilter**<br/>JWT Validation`"]
        CF3["`**InternalTokenFilter**<br/>X-Internal-Token`"]
    end

    %% Service Discovery
    subgraph Discovery["`**Service Discovery**<br/>:8761`"]
        Eureka["`**Eureka Server**<br/>Service Registry`"]
    end

    %% Booking Service
    subgraph Booking["`**Booking Service**<br/>:8083`"]
        BS["`**BookingController**<br/>REST API`"]
        UC["`**UserController**<br/>User Management`"]
        BSS["`**BookingSagaService**<br/>Saga Orchestrator`"]
        HSC["`**HotelServiceClient**<br/>WebClient + Resilience4j`"]
        SCS["`**SagaCompensationService**<br/>Error Recovery`"]
        BDB[("`**Booking Database**<br/>H2 In-Memory<br/>• bookings<br/>• users<br/>• idempotency_records`")]
    end

    %% Hotel Service
    subgraph Hotel["`**Hotel Service**<br/>:8082`"]
        HC["`**HotelController**<br/>Hotel Management`"]
        RC["`**RoomController**<br/>Room Operations`"]
        RS["`**RoomService**<br/>Business Logic`"]
        HDB[("`**Hotel Database**<br/>H2 In-Memory<br/>• hotels<br/>• rooms<br/>• room_slots<br/>• idempotency_records`")]
    end

    %% Client to Gateway
    Client -->|"`**HTTP Requests**<br/>REST API`"| GW

    %% Gateway Filters Chain
    GW --> CF1
    CF1 --> CF2
    CF2 --> CF3

    %% Gateway to Services
    CF3 -->|"`**Booking Operations**<br/>/v1/api/booking/**<br/>/v1/api/user/**`"| BS
    CF3 -->|"`**Hotel Operations**<br/>/v1/api/hotels/**<br/>/v1/api/rooms/**`"| HC

    %% Service Discovery
    GW -.->|"`**Service Discovery**<br/>Load Balancing`"| Eureka
    BS -.->|"`**Register**<br/>Health Checks`"| Eureka
    HC -.->|"`**Register**<br/>Health Checks`"| Eureka

    %% Booking Service Internal Flow
    BS --> BSS
    BSS --> HSC
    BSS --> SCS
    HSC -->|"`**Internal API**<br/>X-Internal-Token<br/>confirmAvailability<br/>confirmBooking<br/>releaseSlot`"| RC
    SCS -->|"`**Compensation**<br/>Error Recovery`"| RC

    %% Database Connections
    BSS --> BDB
    UC --> BDB
    RS --> HDB
    RC --> RS

    %% Styling
    classDef clientStyle fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000
    classDef gatewayStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000
    classDef discoveryStyle fill:#fff3e0,stroke:#f57c00,stroke-width:2px,color:#000
    classDef bookingStyle fill:#e8f5e9,stroke:#388e3c,stroke-width:2px,color:#000
    classDef hotelStyle fill:#e1f5fe,stroke:#0288d1,stroke-width:2px,color:#000
    classDef dbStyle fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:#000

    class Client clientStyle
    class GW,CF1,CF2,CF3 gatewayStyle
    class Eureka discoveryStyle
    class BS,UC,BSS,HSC,SCS bookingStyle
    class HC,RC,RS hotelStyle
    class BDB,HDB dbStyle
Loading

Технологический стек

  • Java 17
  • Spring Boot 3.5.6
  • Spring Cloud 2025.0.0 (Gateway, Eureka)
  • Spring Security с JWT аутентификацией
  • Spring Data JPA + H2 (in-memory)
  • WebFlux (WebClient для межсервисного взаимодействия)
  • Resilience4j (Retry, TimeLimiter)
  • MapStruct (маппинг DTO)
  • Lombok
  • Swagger/OpenAPI (документация)
  • JUnit 5 + Mockito (тестирование)

Ключевые возможности

1. Saga паттерн с компенсацией

  • Двухшаговое подтверждение бронирования: PENDING → CONFIRMED
  • Автоматическая компенсация при сбое: PENDING → CANCELLED с освобождением слота
  • Логирование всех шагов с correlationId для трассировки

2. Идемпотентность операций

  • IdempotencyRecord для отслеживания повторных запросов
  • Защита от дублирования бронирований
  • DLQ (Dead Letter Queue) для анализа сбоев

3. Алгоритм планирования загрузки

  • Сортировка номеров по timesBooked (ASC) для равномерной загрузки
  • Автоподбор номера через параметр autoSelect

4. Защита от race conditions

  • Pessimistic locking (PESSIMISTIC_WRITE) для критических секций
  • Isolation level SERIALIZABLE для подтверждения доступности

5. Устойчивость к сбоям

  • Retry с экспоненциальной задержкой (3 попытки, 500ms)
  • CircuitBreaker для защиты от каскадных сбоев
  • Graceful degradation при недоступности Hotel Service

6. Сквозная трассировка

  • X-Correlation-Id генерируется в Gateway
  • MDC для логирования correlationId во всех сервисах

7. Защита INTERNAL endpoints

  • X-Internal-Token для межсервисного взаимодействия
  • InternalServiceFilter проверяет токен и устанавливает Authentication для Internal Service
  • WebClient автоматически добавляет токен в заголовки запросов
  • Все Internal endpoints защищены: /v1/api/rooms/recommend, /v1/api/rooms/*/confirm-availability, /v1/api/rooms/confirm-booking, /v1/api/rooms/release

8. Централизованное управление API путями

  • Все URL-ы управляются через ApiConstants в shared модуле
  • Единый префикс /v1/api для всех эндпоинтов
  • Типобезопасность и консистентность URL-ов
  • Легкость изменения версии API

Для production рекомендуется:

  • Service Mesh с mTLS (Istio, Linkerd) для автоматической взаимной аутентификации
  • Сетевая изоляция (Kubernetes NetworkPolicy, Docker private networks)
  • Ротация токенов через секреты (K8s Secrets, AWS Secrets Manager)

Архитектурные решения (ADR)

1. Saga Pattern вместо 2PC

Проблема 2PC: Требует блокировки ресурсов на всех сервисах, снижает доступность, плохо масштабируется

Решение: Использовать оркестрацию Saga с компенсацией вместо двухфазного коммита (2PC).

  • Преимущества Saga:
    • Асинхронность - сервисы не блокируются
    • Высокая доступность - сбой одного сервиса не блокирует остальные
    • Простота масштабирования
  • Реализация:
    • PENDING → confirm availability → confirm booking → CONFIRMED
    • При сбое: компенсация через releaseSlot() → CANCELLED

2. X-Internal-Token для межсервисного взаимодействия

Проблема: Internal endpoints (/rooms/recommend, /rooms/*/confirm-availability, /rooms/confirm-booking, /rooms/release) не должны быть доступны извне

Решение: Использовать простой токен в заголовке X-Internal-Token для защиты internal endpoints.

  • Преимущества:
    • Простота реализации для учебного проекта
    • Не требует дополнительной инфраструктуры
  • Недостатки:
    • Токен может быть скомпрометирован
    • Нет автоматической ротации

Для Production можно выбрать другие варианты, например:

  • Service Mesh с mTLS (Istio, Linkerd) - автоматическая взаимная аутентификация
  • Сетевая изоляция (Kubernetes NetworkPolicy) - доступ только внутри кластера
  • Ротация токенов через Kubernetes Secrets или AWS Secrets Manager

3. Pessimistic Locking + SERIALIZABLE Isolation

Проблема: Race conditions при параллельном бронировании одного номера на одни даты

Решение: Использовать пессимистические блокировки (PESSIMISTIC_WRITE) и уровень изоляции SERIALIZABLE для критичных операций.

  • Преимущества:
    • Гарантия отсутствия phantom reads
    • Защита от lost updates
    • Корректная работа при высокой нагрузке
  • Недостатки:
    • Снижение производительности при высоком contention
    • Возможны deadlocks (минимизированы через порядок блокировок)

4. Идемпотентность через requestId

Проблема: Retry механизмы могут привести к дублированию операций

Решение: Каждый запрос маркируется уникальным requestId (используется correlationId), повторные запросы отклоняются.

  • Преимущества:
    • Безопасные повторы запросов
    • Защита от дубликатов при сетевых сбоях
  • Реализация:
    • Таблица idempotency_records с уникальным индексом на request_id
    • Проверка перед выполнением операции
    • Сохранение результата (SUCCESS/FAILED)

Установка и запуск

Предварительные требования

  • JDK 17+
  • Maven 3.6+

Сборка проекта

mvn clean install

Конфигурация

Переменные окружения (опционально)

  • INTERNAL_SERVICE_TOKEN - токен для межсервисного взаимодействия (по умолчанию: dev-internal-secret-token-12345)
    export INTERNAL_SERVICE_TOKEN=your-secure-token-here

Важно: В production используйте криптостойкий токен (например, UUID или сгенерированный через openssl rand -hex 32).

Запуск сервисов (в указанном порядке)

1. Eureka Server

cd eureka-server
mvn spring-boot:run

Проверка: http://localhost:8761

2. Hotel Service

cd hotel-service
mvn spring-boot:run

Проверка: http://localhost:8082/swagger-ui.html

3. Booking Service

cd booking-service
mvn spring-boot:run

Проверка: http://localhost:8083/swagger-ui.html

4. Gateway

cd gateway
mvn spring-boot:run

Проверка: http://localhost:8080


Тестирование API

Postman коллекция

Для удобства тестирования всех эндпойнтов подготовлена Postman коллекция Booking_System.pc.json.

Импорт коллекции:

  1. Откройте Postman
  2. Нажмите Import в верхнем левом углу (или Ctrl + O)
  3. Выберите файл Booking_System.pc.json
  4. Коллекция появится в списке с готовыми запросами

Структура коллекции:

  1. Authentication - регистрация и авторизация (admin/user)
  2. User Management (ADMIN) - CRUD операции с пользователями
  3. Hotels (via Gateway) - управление отелями
  4. Rooms (via Gateway) - управление номерами
  5. Bookings (via Gateway) - создание и управление бронированиями
  6. Internal Saga Endpoints (INTERNAL) - внутренние API для Saga
  7. Direct Service Access - прямой доступ к Swagger, H2 Console, Eureka

Автоматизация:

  • После успешной авторизации JWT токен автоматически сохраняется в переменные коллекции
  • Переменные admin_token и user_token используются для авторизации запросов
  • Настроены три базовых URL: gateway_url, hotel_service_url, booking_service_url

Порядок использования:

  1. Запустите все сервисы (Eureka, Hotel, Booking, Gateway)
  2. Выполните запрос "Login as Admin" или "Login as User" из раздела Authentication
  3. Используйте остальные запросы с автоматически подставленным токеном

Тестирование

Запуск всех тестов

mvn test

Запуск тестов конкретного модуля

cd hotel-service
mvn test

cd booking-service
mvn test

Мониторинг и отладка

Eureka Dashboard

http://localhost:8761

Просмотр зарегистрированных сервисов и их статусов.

Swagger UI

H2 Console (БД в памяти)

Структура БД

Hotel Service (hoteldb)

hotels

Поле Тип Описание
id BIGINT (PK) Уникальный ID отеля
name VARCHAR(255) Название отеля
address VARCHAR(255) Адрес отеля

Индексы: Нет дополнительных (FK автоматически)

rooms

Поле Тип Описание
id BIGINT (PK) Уникальный ID номера
hotel_id BIGINT (FK) ID отеля
number VARCHAR(255) Номер комнаты
available BOOLEAN Операционная доступность (не для бронирования)
times_booked INTEGER Счетчик бронирований (для алгоритма балансировки)

Индексы: Нет дополнительных (FK автоматически)

room_slots

Поле Тип Описание
id BIGINT (PK) Уникальный ID слота
room_id BIGINT (FK) ID номера
start_date DATE Дата начала бронирования
end_date DATE Дата окончания бронирования
request_id VARCHAR(255) Корреляционный ID запроса (идемпотентность)
status VARCHAR(50) Статус слота: PENDING/CONFIRMED/CANCELLED
created_at TIMESTAMP Время создания записи

Индексы:

  • Уникальный: (room_id, request_id) - защита от дубликатов
  • idx_room_dates: (room_id, start_date, end_date) - оптимизация поиска конфликтов

idempotency_records

Поле Тип Описание
id BIGINT (PK) Уникальный ID записи
request_id VARCHAR(255) Уникальный ID запроса
payload TEXT JSON тело запроса
status VARCHAR(50) SUCCESS/FAILED/PENDING
created_at TIMESTAMP Время первой обработки
error_message VARCHAR(255) Сообщение об ошибке (если есть)

Индексы:

  • Уникальный: request_id - быстрая проверка дубликатов

Booking Service (bookingdb)

users

Поле Тип Описание
id BIGINT (PK) Уникальный ID пользователя
username VARCHAR(255) Логин (уникальный)
password VARCHAR(255) BCrypt хэш пароля
role VARCHAR(50) Роль: USER или ADMIN

Индексы:

  • Уникальный: username

bookings

Поле Тип Описание
id BIGINT (PK) Уникальный ID бронирования
user_id BIGINT (FK) ID пользователя
room_id BIGINT ID номера (reference в Hotel Service)
start_date DATE Дата начала
end_date DATE Дата окончания
status VARCHAR(50) PENDING/CONFIRMED/CANCELLED
created_at TIMESTAMP Время создания
correlation_id VARCHAR(255) ID для трассировки Saga и идемпотентности

Индексы:

  • Уникальный: correlation_id - защита от дублирования бронирований
  • idx_user_id: user_id - быстрая выборка бронирований пользователя

idempotency_records

Структура аналогична Hotel Service.


Назначение специальных полей

correlation_id (bookings):

  • Используется как requestId для идемпотентности
  • Прослеживается через все шаги Saga
  • Логируется в MDC для трассировки запросов

request_id (room_slots):

  • Связывает слот с конкретным запросом бронирования
  • Позволяет повторно обрабатывать запрос без создания дубликатов
  • Используется для компенсации при отмене

times_booked (rooms):

  • Счетчик успешных бронирований номера
  • Используется алгоритмом автоподбора для равномерной загрузки
  • Инкрементируется при confirmBooking(), декрементируется при отмене CONFIRMED бронирования

available (rooms):

  • НЕ используется для проверки занятости по датам
  • Отражает операционную доступность (ремонт, вывод из эксплуатации)
  • Занятость определяется через room_slots по датам

Логирование

Все сервисы логируют события с correlationId для трассировки запросов:

[correlationId] Сообщение лога

Пример:

[a3f5b7c9-...] Starting booking saga for user: 2, correlationId: a3f5b7c9-...
[a3f5b7c9-...] Room availability confirmed for correlationId: a3f5b7c9-...
[a3f5b7c9-...] Booking confirmed for correlationId: a3f5b7c9-...

Обработка ошибок

Все ошибки возвращаются в единообразном формате:

{
  "status": 409,
  "message": "Room is already booked for the selected dates",
  "timestamp": "2025-10-20T12:34:56"
}

Коды ответов:

  • 200 - Успех
  • 201 - Создан
  • 204 - Нет содержимого (успешное удаление)
  • 400 - Некорректный запрос
  • 401 - Неавторизован
  • 403 - Доступ запрещен
  • 404 - Не найдено
  • 409 - Конфликт (номер занят, дубликат запроса)
  • 500 - Внутренняя ошибка сервера

Сценарии тестирования

Позитивный сценарий

  1. Регистрация пользователя → получение JWT
  2. Получение списка доступных номеров
  3. Создание бронирования (с указанием номера или автоподбором)
  4. Просмотр истории бронирований
  5. Отмена бронирования

Негативные сценарии

  1. Попытка бронирования уже занятого номера → 409 Conflict
  2. Попытка доступа к чужому бронированию → 403 Forbidden
  3. Попытка создания отеля пользователем (не ADMIN) → 403 Forbidden
  4. Попытка бронирования без токена → 401 Unauthorized
  5. Сбой Hotel Service → компенсация, статус CANCELLED

Тестирование идемпотентности

  1. Повторная отправка одного и того же запроса → отклонение дубликата
  2. Повторное подтверждение доступности номера → игнорирование

Тестирование алгоритма планирования

  1. Создание нескольких бронирований с autoSelect
  2. Проверка, что номера выбираются с наименьшим timesBooked
  3. Проверка, что timesBooked инкрементируется после каждого бронирования

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages