A Spring Boot microservices backend, organized as a single Gradle multi-module monorepo.
Floating Lyric is a backend made of small, independently deployable services that share common code through plain library modules. Everything lives in one repository ("monorepo") so that build configuration and shared contracts stay in sync — but each service still builds its own runnable jar and is deployed on its own.
| Module | Type | Description |
|---|---|---|
services/core |
Service | Core application service. |
services/auth |
Service | Authentication service. |
libs/contracts |
Library | Shared DTOs / API models that cross service boundaries. |
build-logic |
Build logic | Convention plugins holding the shared Gradle setup. |
Note
Services communicate with each other over the network (HTTP/gRPC/messaging), not by depending on each other's code. The only thing they share at compile time is library modules under libs/.
- Language: Kotlin 2.3.21 (JVM), targeting the Java 25 toolchain
- Framework: Spring Boot 4.1.0
- Build: Gradle 9.5.1 with the Kotlin DSL and a wrapper (no local Gradle install needed)
- Testing: JUnit 5 + Spring Boot Test
- JDK 25 (the build's Java toolchain). Verify with
java -version. - That's it — the included Gradle wrapper (
./gradlew) downloads the correct Gradle version automatically.
# Build and test every module
./gradlew build
# Run a single service
./gradlew :services:core:bootRun
./gradlew :services:auth:bootRunImportant
Both services default to port 8080, so they can't run at the same time out of the box. Give one a different port before running them together, e.g.:
./gradlew :services:auth:bootRun --args='--server.port=8081'or set server.port in that service's src/main/resources/application.properties.
floating-lyric-spring-boot/
├── build-logic/ # Shared Gradle build logic (convention plugins)
│ └── src/main/kotlin/
│ ├── floating-lyric.spring-service.gradle.kts # config for runnable services
│ └── floating-lyric.kotlin-library.gradle.kts # config for shared libraries
├── libs/ # Shared libraries (plain jars services compile against)
│ └── contracts/
├── services/ # Deployable Spring Boot services
│ ├── auth/
│ └── core/
├── gradle/ # Gradle wrapper
├── settings.gradle.kts # Lists every module in the build
└── gradlew / gradlew.bat
Shared configuration (Kotlin, Spring Boot, the Java toolchain, test setup) is defined once in build-logic as convention plugins, so module build files stay tiny:
// services/auth/build.gradle.kts
plugins {
id("floating-lyric.spring-service")
}
dependencies {
implementation(project(":libs:contracts")) // depend on a shared library
}Bumping a shared version (Kotlin, Spring Boot, Java) is a one-line change in the convention plugin and applies to every module.
| Command | Description |
|---|---|
./gradlew build |
Compile and test all modules |
./gradlew test |
Run all tests |
./gradlew :services:core:bootRun |
Run the core service |
./gradlew :services:auth:bootRun |
Run the auth service |
./gradlew :services:auth:bootJar |
Build a runnable jar for auth |
./gradlew projects |
Print the module tree |
services/auth is a standalone Spring Boot service providing user registration, email verification, login, and JWT-based session management.
| Endpoint | Auth | Request body | Success | Notes |
|---|---|---|---|---|
POST /auth/register |
none | {email, password, displayName?} |
201 |
Creates an unverified user; sends a verification email. |
POST /auth/verify-email |
none | {token} |
200 |
Marks the user verified; consumes the token. |
POST /auth/resend-verification |
none | {email} |
202 |
Always 202 (no account enumeration). |
POST /auth/login |
none | {email, password} |
200 |
Returns TokenResponse; 403 if email not verified. |
POST /auth/refresh |
none | {refreshToken} |
200 |
Rotates the refresh token (TokenResponse). |
POST /auth/logout |
none | {refreshToken} |
204 |
Revokes the presented refresh token. |
POST /auth/password/forgot |
none | {email} |
202 |
Sends a reset link. Always 202 (no enumeration). |
POST /auth/password/reset |
none | {token, newPassword} |
200 |
Sets new password; revokes all refresh tokens. |
POST /auth/password/change |
Bearer | {oldPassword, newPassword} |
200 |
Authenticated self-service change. |
GET /auth/me |
Bearer | — | 200 |
Returns {id, email, displayName, emailVerified}. |
GET /.well-known/jwks.json |
none | — | 200 |
Public RSA key set for validators. |
Access tokens are RS256 JWTs; public keys are exposed via the JWKS endpoint so any other service can verify them without a shared secret.
Warning
services/auth/src/main/resources/keys/dev-signing-key.pem is a committed DEV-ONLY RSA key for local development. Never use it in production — generate and inject a fresh private key via environment variable or secrets manager before deploying.
- Create the folder and a minimal build file:
// services/<name>/build.gradle.kts plugins { id("floating-lyric.spring-service") }
- Add your
@SpringBootApplicationclass underservices/<name>/src/main/kotlin/.... - Register the module in
settings.gradle.kts:include("services:<name>")
Add a shared library the same way using the floating-lyric.kotlin-library plugin under libs/.
Tip
Put models that more than one service needs (request/response DTOs, events) in libs/contracts so callers don't hand-roll their own copies.
This repo is one of several that make up Floating Lyric:
- 📱 App — flutter-floating-lyric
- 🌐 Landing — floating-lyric-web
- 🔥 Firebase backend — flutter-floating-lyric-firebase-cloud-function
- 🌱 Spring Boot backend — floating-lyric-spring-boot ← this repo
- 📄 API contract — flutter-floating-lyric-openapi
- 📦 Generated DTOs — flutter-floating-lyric-pkg-generated-openapi
📋 Work across all repos is tracked on the Floating Lyric — Product board.