Architecture - jegr78/ctc-manager GitHub Wiki

Architecture

Tech Stack

Component Technology
Language Java 25
Framework Spring Boot 4.x
Build Maven
Admin UI Thymeleaf (server-side rendering)
Database MariaDB (prod/local/docker), H2 in-memory (dev)
Migrations Flyway
Unit Tests JUnit 5, Mockito
E2E Tests Playwright, Jsoup
External API Google Sheets API v4

Package Structure

org.ctc
ā”œā”€ā”€ admin
│   ā”œā”€ā”€ controller      # Admin CRUD controllers (Season, Matchday, Race, Team, Driver, ...)
│   └── dto             # Form and display DTOs (CarForm, RaceForm, RaceResultForm, ...)
ā”œā”€ā”€ dataimport          # CSV/Google Sheets import, fuzzy driver matching
│   ā”œā”€ā”€ CsvImportController / CsvImportService
│   ā”œā”€ā”€ GoogleSheetsService
│   ā”œā”€ā”€ ScorecardParser
│   └── DriverMatchingService
ā”œā”€ā”€ domain
│   ā”œā”€ā”€ model           # JPA Entities
│   ā”œā”€ā”€ repository      # Spring Data JPA Repositories
│   └── service         # Business logic (Scoring, Standings, Rankings, Playoffs, Swiss Pairing)
ā”œā”€ā”€ gt7sync             # GT7 car/track scraping and sync
│   ā”œā”€ā”€ Gt7ScraperService / Gt7SyncService
│   └── Gt7SyncController
└── sitegen             # Static site generation for GitHub Pages
    ā”œā”€ā”€ SiteGeneratorService
    ā”œā”€ā”€ SiteGeneratorController
    └── model           # View models for generated pages

Data Model

Core Racing Hierarchy

Season
 └── Matchday
      └── Race
           └── RaceResult (per driver: position, quali, fastest lap)

Teams and Drivers

Team
 └── Team (sub-teams, self-referencing parent)

Driver (PSN ID, nickname)
 └── SeasonDriver (driver ↔ season assignment with team)

MatchdayLineup (which drivers race on a given matchday)

Playoffs

Playoff (linked to Season)
 └── PlayoffRound (round of 8, quarterfinal, semifinal, final, ...)
      └── PlayoffMatchup (team vs team, best-of-legs)

Supporting Entities

Entity Purpose
Car Gran Turismo car master data (name, manufacturer, GT7 ID, image)
Track Gran Turismo track master data (name, GT7 ID, image)
RaceAttachment Files or links attached to a race
AttachmentType Enum: FILE, LINK
SeasonFormat Enum: LEAGUE, SWISS

Entity Relationships

Season ──1:N──> Matchday ──1:N──> Race ──1:N──> RaceResult
Season ──1:N──> SeasonDriver ──N:1──> Driver
Season ──1:N──> Playoff ──1:N──> PlayoffRound ──1:N──> PlayoffMatchup
Race ──N:1──> Track
Race ──1:N──> RaceAttachment
RaceResult ──N:1──> Driver
RaceResult ──N:1──> Car
Team ──1:N──> Team (sub-teams)
Matchday ──1:N──> MatchdayLineup ──N:1──> SeasonDriver

Spring Profiles

Profile Database Port Purpose
dev H2 in-memory 9090 Development, tests
dev,demo H2 in-memory 9090 Like dev, auto-imports all GT7 cars and tracks with images
local MariaDB (localhost) 9091 Local MariaDB development
docker MariaDB (container db) 8080 Docker Compose deployment
prod Cloud DB (env vars) 8080 Production deployment

OSIV (Open Session in View)

OSIV is intentionally enabled (spring.jpa.open-in-view=true). The Hibernate session stays open until the HTTP request completes, allowing Thymeleaf templates to render lazy-loaded entity fields without requiring eager fetching or DTOs for every view. This is the correct approach for a server-side rendered admin application.

Scoring System

Race Points (by finishing position)

Position 1st 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th
Points 20 17 14 12 10 8 7 6 5 4 3 2

Bonus Points

Category 1st 2nd 3rd
Qualifying 3 2 1
Fastest Lap 2 - -