Распределённая система бронирования отелей на базе Spring Boot с использованием микросервисной архитектуры.
Система состоит из 4 модулей:
┌─────────────┐
│ Client │
└──────┬──────┘
│
▼
┌─────────────────┐ ┌──────────────┐
│ API Gateway │◄────►│ Eureka │
│ (Port 8084) │ │ (Port 8761) │
└────────┬────────┘ └──────────────┘
│
┌────┴────┐
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ Booking │ │ Hotel │
│ Service │◄┤ Service │
│ (8083) │ │ (8082) │
└─────────┘ └─────────┘
- Eureka Server (8761) - Service Discovery
- API Gateway (8084) - Точка входа, маршрутизация
- Hotel Service (8082) - Управление отелями и номерами
- Booking Service (8083) - Бронирования, аутентификация пользователей
- Java 17
- Spring Boot 3.3.5
- Spring Cloud 2023.0.3
- Spring Security + JWT
- Spring Data JPA + H2 (in-memory)
- Spring Cloud Gateway
- Spring Cloud Netflix Eureka
- OpenFeign для межсервисного взаимодействия
- Spring Retry для устойчивости
- Lombok
- JUnit 5 + MockMvc
Запускать модули строго в указанном порядке:
# 1. Eureka Server
cd eureka-server
../gradlew bootRun
# 2. Hotel Service (в новом терминале)
cd hotel-service
../gradlew bootRun
# 3. Booking Service (в новом терминале)
cd booking-service
../gradlew bootRun
# 4. API Gateway (в новом терминале)
cd api-gateway
../gradlew bootRunПодождите 10-20 секунд после запуска каждого сервиса для регистрации в Eureka.
- Eureka Dashboard: http://localhost:8761
- Swagger Hotel Service: http://localhost:8082/swagger-ui.html
- Swagger Booking Service: http://localhost:8083/swagger-ui.html
curl -X POST http://localhost:8084/user/register \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "test123"}'Ответ:
{
"token": "eyJhbGc...",
"username": "testuser",
"role": "USER"
}curl -X POST http://localhost:8084/user/auth \
-H "Content-Type: application/json" \
-d '{"username": "user", "password": "user"}'Предзаполненные пользователи:
admin/admin(роль: ADMIN)user/user(роль: USER)
curl -X GET http://localhost:8084/api/hotels \
-H "Authorization: Bearer YOUR_TOKEN"curl -X GET http://localhost:8084/api/rooms/recommend \
-H "Authorization: Bearer YOUR_TOKEN"curl -X POST http://localhost:8084/booking \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"startDate": "2025-11-01",
"endDate": "2025-11-05",
"autoSelect": true
}'curl -X POST http://localhost:8084/booking \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"roomId": 1,
"startDate": "2025-11-01",
"endDate": "2025-11-05",
"autoSelect": false
}'curl -X GET http://localhost:8084/bookings \
-H "Authorization: Bearer YOUR_TOKEN"curl -X DELETE http://localhost:8084/booking/1 \
-H "Authorization: Bearer YOUR_TOKEN"curl -X POST http://localhost:8084/api/hotels \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "New Hotel",
"address": "123 Street"
}'curl -X POST http://localhost:8084/api/rooms \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"hotelId": 1,
"number": "401"
}'Реализован алгоритм равномерного распределения нагрузки на номера:
- Каждый номер имеет счётчик
timesBooked - При запросе рекомендаций возвращаются номера, отсортированные по возрастанию
timesBooked, затем поid - При создании бронирования с
autoSelect: trueавтоматически выбирается наименее загруженный номер - Это предотвращает «простой» номеров и обеспечивает равномерную загрузку
Реализован двухшаговый процесс бронирования:
- PENDING: Booking Service создаёт запись бронирования со статусом PENDING
- Подтверждение: Вызов Hotel Service для подтверждения доступности (
POST /api/rooms/{id}/confirm-availability) - CONFIRMED: При успехе статус меняется на CONFIRMED
- Компенсация: При ошибке:
- Статус меняется на CANCELLED
- Вызывается
POST /api/rooms/{id}/releaseдля отката изменений в Hotel Service
- Используется
@Retryableс экспоненциальным backoff - Максимум 3 попытки с увеличивающимся интервалом (500ms × 2)
- Timeout на Feign клиентах: 2 секунды
- При исчерпании попыток выполняется компенсация
- Каждый запрос бронирования получает уникальный
requestId - Hotel Service хранит обработанные
requestIdв памяти - Повторные запросы с тем же
requestIdигнорируются - Это предотвращает дубликаты при повторных попытках
- API Gateway добавляет заголовок
X-Correlation-Idко всем запросам - В логах используется
bookingIdдля отслеживания саги - Все ключевые шаги логируются: создание, подтверждение, компенсация
- JWT токены со сроком действия 1 час
- Роли: USER (личные операции) и ADMIN (управление системой)
- Каждый сервис самостоятельно валидирует JWT (Resource Server pattern)
@PreAuthorizeна методах контроллеров- Корректные HTTP статусы: 401 (Unauthorized), 403 (Forbidden)
Запуск тестов:
# Все тесты
./gradlew test
# Только один модуль
./gradlew :hotel-service:test
./gradlew :booking-service:testРеализованы тесты:
- Позитивные сценарии: регистрация, аутентификация, создание бронирования
- Негативные сценарии: неверные credentials, невалидные даты, доступ без прав
- Интеграционные: проверка саги, компенсации, retry
- Security: проверка ролей ADMIN/USER
- 3 отеля: Grand Hotel, City Inn, Resort Paradise
- 15 номеров (по 5 на отель)
- admin / admin (ADMIN)
- user / user (USER)
Распределённые транзакции (2PC) сложны, требуют координатора и блокируют ресурсы. Сага с компенсацией проще, более отказоустойчива и лучше подходит для микросервисов.
Это паттерн Resource Server - каждый сервис независим и может работать без Gateway. Gateway остаётся простым маршрутизатором.
Для демонстрации и тестирования. В production заменить на PostgreSQL/MySQL без изменения кода (только application.yml).
Feign декларативный, интегрирован с Spring Cloud (service discovery, load balancing), проще в использовании.
Каждый сервис следует layered architecture:
com.booking.{service}
├── entity # JPA сущности
├── repository # Spring Data репозитории
├── service # Бизнес-логика
├── controller # REST endpoints
├── dto # Data Transfer Objects
├── security # JWT фильтры, конфигурация
├── client # Feign клиенты (только Booking)
├── config # Конфигурация, инициализация данных
└── exception # Обработка ошибок