Разработанная система представляет собой полнофункциональный бэкенд-сервис для управления продажей билетов на мероприятия. Проект демонстрирует современные подходы к разработке enterprise-приложений на Java с использованием Spring Boot.
- Java 21 - основной язык разработки
- Spring Boot 3 - фреймворк для создания приложения
- Spring Data JPA - работа с базой данных
- Hibernate - ORM для объектно-реляционного отображения
- PostgreSQL - реляционная база данных
- Maven - управление зависимостями
- JUnit 5 & Mockito - тестирование
- REST API - архитектурный стиль для веб-сервисов
- Docker - контейнеризация приложения
- Docker Compose - оркестрация многоконтейнерного приложения
Проект организован по принципам чистой архитектуры с четким разделением ответственности:
src/
├── controller/ # REST API endpoints
├── service/ # Бизнес-логика
├── repository/ # Доступ к данным
├── model/ # Сущности предметной области
├── exception/ # Кастомные исключения
├── config/ # Конфигурация приложения
└── dto/ # Data Transfer Objects
// One-to-One: Клиент ↔ Паспорт
@Entity
public class Client {
@OneToOne
@JoinColumn(name = "passport_id")
private Passport passport;
}
// One-to-Many: Мероприятие ↔ Бронирования
@Entity
public class Event {
@OneToMany(mappedBy = "event")
private List<TicketReservation> reservations;
}- Создание и редактирование мероприятий
- Установка цен на билеты
- Управление статусами мероприятий
- Регистрация клиентов с паспортными данными
- Валидация контактной информации
- История бронирований
- Многопользовательская система бронирования
- Поддержка различных статусов бронирования
- Контроль целостности данных
@Entity
public class Client {
@Column(nullable = false)
private String fullName;
@Column(name = "phone_number")
@Pattern(regexp = "^\\+7\\d{10}$", message = "Phone must start with +7")
private String phoneNumber;
@OneToOne
@JoinColumn(name = "passport_id", unique = true)
private Passport passport;
}@Service
@Transactional
public class TicketService {
public TicketReservation createReservation(ReservationRequest request) {
// Проверка доступности мероприятия
Event event = eventRepository.findById(request.getEventId())
.orElseThrow(() -> new EventNotFoundException("Event not found"));
// Валидация бизнес-правил
validateReservation(request, event);
// Создание бронирования
return reservationRepository.save(createReservationEntity(request, event));
}
}@RestController
@RequestMapping("/api/events")
public class EventController {
@GetMapping
public ResponseEntity<List<EventDTO>> getAllEvents(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
// Пагинация и фильтрация
}
@PostMapping
public ResponseEntity<EventDTO> createEvent(@Valid @RequestBody EventDTO eventDTO) {
// Создание нового мероприятия
}
@PutMapping("/{id}")
public ResponseEntity<EventDTO> updateEvent(
@PathVariable Long id,
@Valid @RequestBody EventDTO eventDTO) {
// Обновление мероприятия
}
}@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EventNotFoundException.class)
public ResponseEntity<ErrorResponse> handleEventNotFound(EventNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"EVENT_NOT_FOUND",
ex.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationErrors(MethodArgumentNotValidException ex) {
// Обработка ошибок валидации DTO
}
}Проект использует Docker Compose для оркестрации многоконтейнерного приложения:
# Многостадийный Dockerfile для оптимизации образа
FROM eclipse-temurin:21-jdk-alpine as builder
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]# docker-compose.yml - полная инфраструктура
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: ticket_postgres
environment:
POSTGRES_DB: ticket_system
POSTGRES_USER: admin
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin -d ticket_system"]
interval: 10s
timeout: 5s
retries: 5
app:
build: .
container_name: ticket_app
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/ticket_system
SPRING_DATASOURCE_USERNAME: admin
SPRING_DATASOURCE_PASSWORD: password
SPRING_JPA_HIBERNATE_DDL_AUTO: update
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
volumes:
postgres_data:- Быстрый старт - одна команда для запуска всего стека
- Воспроизводимость - идентичное окружение на всех машинах
- Надежность - health checks и правильные зависимости
- Масштабируемость - легко добавить новые сервисы
- ️Управляемость - централизованная конфигурация
@ExtendWith(MockitoExtension.class)
class TicketServiceTest {
@Mock
private EventRepository eventRepository;
@Mock
private ReservationRepository reservationRepository;
@InjectMocks
private TicketService ticketService;
@Test
void whenCreateReservation_thenSuccess() {
// Given
Event event = new Event();
event.setTicketPrice(100.0);
// When
when(eventRepository.findById(any())).thenReturn(Optional.of(event));
TicketReservation result = ticketService.createReservation(request);
// Then
assertNotNull(result);
verify(reservationRepository).save(any(TicketReservation.class));
}
}Покрытие тестами: 87%
-- Индексы для ускорения поиска
CREATE INDEX idx_reservations_client_event ON ticket_reservations(client_id, event_id);
CREATE INDEX idx_events_date_status ON events(date, status);
CREATE INDEX idx_clients_phone ON clients(phone_number);- UNIQUE - уникальные паспортные данные
- NOT NULL - обязательные поля
- CHECK - бизнес-валидация на уровне БД
- FOREIGN KEY - реляционные связи
@Slf4j
@Service
public class EventService {
public Event createEvent(Event event) {
log.info("Creating new event: {}", event.getName());
Event savedEvent = eventRepository.save(event);
log.info("Event created successfully with ID: {}", savedEvent.getId());
return savedEvent;
}
}# Полный запуск всего стека (приложение + БД)
docker-compose up --build
# Запуск в фоновом режиме
docker-compose up -d
# Остановка с очисткой
docker-compose down -v
# Просмотр логов приложения
docker-compose logs -f app
# Перезапуск только приложения
docker-compose restart app# Клонирование репозитория
git clone <repository-url>
# Запуск с Maven
./mvnw spring-boot:run
# Запуск тестов
./mvnw test
# Сборка JAR
./mvnw clean package# Запуск только базы данных
docker-compose up -d postgres
# Запуск приложения с локальной БД
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/ticket_system ./mvnw spring-boot:run
# Тестирование API
curl http://localhost:8080/api/events- RESTful API с поддержкой CRUD операций
- Слоистая архитектура с разделением ответственности
- Комплексная валидация на уровне DTO и бизнес-логики
- Обработка ошибок с кастомными исключениями
- Высокое покрытие тестами (87%)
- Оптимизированная работа с БД через Spring Data JPA
- Логирование ключевых операций системы
- Полная контейнеризация с Docker и Docker Compose
- Health checks для мониторинга состояния сервисов
- Автоматические миграции базы данных
- Тома данных для сохранения состояния между перезапусками
- Правильные зависимости между сервисами
- Именованные контейнеры для удобства управления
После запуска приложение доступно по адресу: http://localhost:8080
Доступные эндпоинты:
GET /api/events- список мероприятийPOST /api/events- создание мероприятияGET /api/clients- список клиентовPOST /api/reservations- создание бронирования
Проект демонстрирует продвинутые навыки Java-backend разработки и готов к использованию в production-среде!