This project is built so you can learn, test, and modify small examples instead of reading a large codebase.
Full repo learning notes are in docs/README.md. Deployment notes and production files are in deploy/README.md.
The repo now teaches two architectural styles:
- a simple layered flow for the student example
- a ports-and-adapters style for the course example
Read docs/architecture.md for the design rationale and package boundaries.
- classes and objects
- methods
- variables and types
ifandfor- constructors
ListandMap- exceptions
- packages
- annotations
- interfaces
- inheritance and polymorphism
- generics
- streams and lambdas
- controller
- service
- dependency injection
- request/response model
- JSON request/response flow
- Swagger UI
- OpenAPI YAML
- validation
- MongoDB persistence
- basic authentication with Spring Security
- unit and web-layer tests
- enums
@ConfigurationProperties
- Vue 3 + TypeScript with Vite
- Vuetify UI layer
- Pinia state management
- Vue Router navigation
- backend integration with the student and course APIs
- a UI workflow for learning, testing, and modifying the backend
Read these files in order:
src/main/java/com/example/demo/basics/model/Student.javasrc/test/java/com/example/demo/basics/model/StudentTest.javasrc/main/java/com/example/demo/basics/interfaces/Notifier.javasrc/main/java/com/example/demo/basics/inheritance/LearningStudent.javasrc/main/java/com/example/demo/basics/generics/Box.javasrc/main/java/com/example/demo/basics/streams/StudentAnalytics.javasrc/main/java/com/example/demo/spring/service/StudentService.javasrc/main/java/com/example/demo/spring/persistence/domain/Course.javasrc/main/java/com/example/demo/spring/persistence/service/CourseService.javasrc/main/java/com/example/demo/spring/persistence/service/CourseApplicationService.javasrc/main/java/com/example/demo/spring/persistence/store/MongoCourseStore.javasrc/main/java/com/example/demo/spring/persistence/controller/CourseController.javasrc/main/java/com/example/demo/spring/config/AppLearningProperties.javasrc/test/java/com/example/demo/spring/persistence/controller/CourseControllerTest.java
com.example.demo.basics.annotations- custom annotation example
com.example.demo.basics.exceptions- custom exception example
com.example.demo.basics.model- plain Java class with fields, constructors, methods,
if,for,List, andMap
- plain Java class with fields, constructors, methods,
com.example.demo.basics.interfaces- interface and implementation examples
com.example.demo.basics.inheritance- inheritance and polymorphism examples
com.example.demo.basics.generics- generic class example
com.example.demo.basics.streams- streams and lambda-style collection processing
com.example.demo.spring.repository- in-memory data storage
com.example.demo.spring.service- business logic
com.example.demo.spring.controller- HTTP endpoints and error handling
com.example.demo.spring.model- request and response models
com.example.demo.spring.persistence- validation, MongoDB documents, layered architecture, and security examples
com.example.demo.spring.config- OpenAPI config and typed configuration properties
Start MongoDB first:
docker compose up -d mongoThen run the app:
./gradlew bootRunThe app starts on http://localhost:8080.
The frontend lives in frontend/README.md.
For a one-command local preview of frontend + backend together, run:
./scripts/start-dev.shThat will:
- start MongoDB with Docker Compose
- start the Spring Boot backend on
http://localhost:8080 - start the Vue frontend on
http://localhost:5173
Press Ctrl+C to stop the frontend and backend process. MongoDB stays available in Docker.
Manual start is also available if you want separate terminals.
If MongoDB is already running locally and you do not want Docker in the loop, use:
./scripts/start-local-dev.shThat script:
- assumes MongoDB is already reachable on
localhost:27017 - stops old repo-owned Java and Vite processes
- starts the backend on
http://localhost:8080 - starts the frontend on
http://localhost:5173
To stop any previous repo dev processes without starting again, run:
./scripts/stop-dev.shStart the backend first, then run:
cd frontend
npm install
npm run devThe frontend runs on http://localhost:5173 and proxies API requests to the backend on http://localhost:8080.
This repo now includes a simple production deployment target using Docker Compose and MongoDB.
Main files:
deploy/docker-compose.prod.ymldeploy/app.env.examplesrc/main/resources/application-prod.yml
Main production differences:
- MongoDB for persisted course documents
prodSpring profile- externalized credentials
- public
/actuator/healthendpoint for health checks - Swagger UI disabled in production
Quick start:
cp deploy/app.env.example deploy/app.env
docker compose --env-file deploy/app.env -f deploy/docker-compose.prod.yml up -d --build
curl http://localhost:8080/actuator/healthBuild and start the app container:
docker compose up --build appThen open:
http://localhost:8080/api/studentshttp://localhost:8080/swagger-ui.htmlhttp://localhost:8080/api/courses
To stop it:
docker compose downGet the seeded students:
curl http://localhost:8080/api/studentsGet one student:
curl http://localhost:8080/api/students/1Create a student:
curl -X POST http://localhost:8080/api/students \
-H "Content-Type: application/json" \
-d '{
"name": "Lina",
"age": 23,
"active": true,
"subjects": ["annotations", "controller"],
"scores": {
"java": 91,
"spring": 89
}
}'Get secured courses with basic auth:
curl -u student:password http://localhost:8080/api/coursesShow typed configuration properties:
curl http://localhost:8080/api/learning-infoCreate a secured MongoDB-backed course:
curl -u student:password -X POST http://localhost:8080/api/courses \
-H "Content-Type: application/json" \
-d '{
"title": "Spring Data MongoDB",
"level": "INTERMEDIATE",
"durationInHours": 7,
"published": true
}'Update a course:
curl -u student:password -X PUT http://localhost:8080/api/courses/course-101 \
-H "Content-Type: application/json" \
-d '{
"title": "Updated Java Generics",
"level": "ADVANCED",
"durationInHours": 10,
"published": false
}'Delete a course:
curl -u student:password -X DELETE http://localhost:8080/api/courses/course-202After starting the app with ./gradlew bootRun, open:
- Swagger UI:
http://localhost:8080/swagger-ui.html - OpenAPI JSON:
http://localhost:8080/v3/api-docs - OpenAPI YAML:
http://localhost:8080/v3/api-docs.yaml
Swagger UI is generated from the controller and model annotations in the code.
To export the generated YAML to a file automatically, run:
./gradlew exportOpenApiYamlThat writes:
openapi.yaml
./gradlew testRun the tests inside a container:
docker compose run --rm testDifferent test layers in this repo:
- unit tests: small classes without Spring, such as
StudentTestandCourseServiceTest - configuration tests: focused tests such as
CourseDataInitializerTest - web/controller tests:
StudentControllerTest,CourseControllerTest,OpenApiDocumentationTest, andLearningInfoControllerTest
This repo includes a GitHub Actions workflow at .github/workflows/ci.yml.
It runs on every push and pull request, sets up Java 17, and executes:
./gradlew test --no-daemon- In
Student.java, change howlevel()works. - In
Student.java, add another method such asaverageScore(). - In
StudentService.java, change how new students are created. - In
StudentController.java, add a new endpoint such asDELETE /api/students/{id}. - In
CourseController.java, add a new secured endpoint such asDELETE /api/courses/{id}. - In
CourseControllerTest.java, add a failing test before you change the code.
- Constructors:
Student(...) - Methods:
enroll,addScore,level,totalScore if:enroll,addScore,levelfor:totalScore,subjectSummary, service mapping methodsList: subjectsMap: scores- Interfaces:
Notifier,EmailNotifier - Inheritance:
Person,LearningStudent - Generics:
Box<T> - Streams:
StudentAnalytics - Enums:
CourseLevel - Exceptions:
InvalidScoreException,StudentNotFoundException - Annotations:
@LearningExample,@Service,@RestController,@RestControllerAdvice,@Document,@Valid - Dependency injection:
StudentControllergetsStudentService,StudentServicegetsInMemoryStudentRepository - Validation:
CreateCourseRequest - Persistence:
CourseDocument,CourseRepository, MongoDB - Security: HTTP basic auth for
/api/courses/**with userstudent/password - Typed configuration:
AppLearningPropertiesbound fromapplication.yml