This is a demo project for a Spring Boot application that manages users, courses, and learning platforms. It showcases a dual-database architecture using MySQL for transactional data (writes) and MongoDB for denormalized, read-optimized data.
- RESTful API: Exposes endpoints for CRUD operations on Users, Courses, and Platforms.
- Dual-Database Architecture:
- MySQL: Acts as the source of truth for all transactional data (Users, Courses, Platforms).
- MongoDB: Stores denormalized
Platformdocuments, embedding courses and enrolled users for efficient read operations.
- Asynchronous Data Synchronization: Changes made to the SQL database (e.g., creating a user, enrolling in a course) trigger an asynchronous process to update the corresponding documents in MongoDB.
- User Management: Create, read, update, and delete users.
- Course Management: Create, read, update, and delete courses.
- Platform Management: Create, read, update, and delete platforms, which are collections of courses.
- Enrollment System: Allows users to be enrolled in multiple courses.
- Validation: Input DTOs are validated at the controller level.
- Global Exception Handling: Centralized handling for API errors.
- Backend: Java 17, Spring Boot 3.5.0
- Data Persistence:
- Spring Data JPA & Hibernate
- Spring Data MongoDB
- Databases:
- MySQL
- MongoDB
- Build Tool: Apache Maven
- DevTools: Spring Boot DevTools for live reload.
Before you begin, ensure you have the following installed:
- JDK 17
- Apache Maven
- MySQL Server
- MongoDB Community Server
-
Clone the repository:
git clone https://github.com/Maou3434/java_intern.git cd java_intern -
Configure Databases:
- Ensure your MySQL and MongoDB servers are running.
- Create a new database in MySQL named
springboot_jpa_demo.CREATE DATABASE springboot_jpa_demo;
- The application will connect to a MongoDB database named
dbtest. MongoDB will create it automatically on the first connection if it doesn't exist.
-
Set Environment Variables: The application uses environment variables for database credentials. Set them in your operating system or IDE's run configuration.
DB_USERNAME: Your MySQL username.DB_PASSWORD: Your MySQL password.
Alternatively, you can modify
src/main/resources/application.propertiesdirectly, but using environment variables is recommended.# src/main/resources/application.properties spring.datasource.username=your_mysql_username spring.datasource.password=your_mysql_password
-
Build and Run the Application: Use the Maven wrapper to build and run the project.
./mvnw spring-boot:run
The application will start on
http://localhost:8080.
The application exposes the following REST endpoints:
- Users:
/api/users - Courses:
/api/courses - Platforms:
/api/platforms
Each endpoint supports standard CRUD operations (GET, POST, PUT, DELETE).
The application follows a layered architecture (Controller -> Service -> Repository). A key feature is the data synchronization from MySQL to MongoDB. Here are the data flows for key operations.
This flow demonstrates how data is written to MySQL and then asynchronously synced to MongoDB.
POST /api/platforms
-
PlatformController:- Receives the HTTP POST request with a
PlatformDTOin the body. - Validates the DTO.
- Uses
PlatformMapperto convert thePlatformDTOinto aPlatformJPA entity. This involves fetching associatedCourseentities from the database via thePlatformService. - Calls
platformService.createPlatform()with thePlatformentity. - Maps the returned, persisted
Platformentity back to aPlatformDTOand sends it in the HTTP response.
- Receives the HTTP POST request with a
-
PlatformService:- Receives the
Platformentity. - Within a
@Transactionalmethod, it sets the bidirectional relationship by linking eachCourseback to thePlatform. - Calls
platformRepository.save(platform)to persist the new platform and its associated courses to the MySQL database. - Calls
platformSyncService.syncToMongo(savedPlatform)to trigger an asynchronous update to MongoDB.
- Receives the
-
PlatformSyncService:- This service runs in a separate thread (
@EnableAsync). - It receives the
Platformentity from thePlatformService. - It constructs a
PlatformDocument(a MongoDB document). This involves:- Fetching all users enrolled in the platform's courses from MySQL via
UserRepository. - Creating
CourseEmbedandUserEmbedobjects to create a denormalized document.
- Fetching all users enrolled in the platform's courses from MySQL via
- Calls
platformDocRepository.save(doc)to save the complete, denormalizedPlatformDocumentto the MongoDB database.
- This service runs in a separate thread (
This flow demonstrates a read-optimized query directly from MongoDB.
GET /api/platforms/{mongoId}/users
-
PlatformController:- Receives the HTTP GET request with the platform's MongoDB ID (
mongoId). - Calls
platformService.getUsersByPlatformIdFromMongo(mongoId). - Receives a list of
UserDTOs and returns them in the HTTP response.
- Receives the HTTP GET request with the platform's MongoDB ID (
-
PlatformService:- Calls
platformDocRepository.findById(mongoId)to fetch thePlatformDocumentdirectly from MongoDB. - If the document is found, it extracts the embedded user information from all courses within the document.
- It aggregates this data and maps it to a list of
UserDTOs.
- Calls
This flow shows how an update to one entity (User) can trigger a sync of another (Platform).
POST /api/users/{id}/courses
-
UserController:- Receives the user ID and a set of
courseIds. - Calls
userService.enrollUserInCourses(userId, courseIds). - Maps the updated
Userentity to aUserDTOand returns it.
- Receives the user ID and a set of
-
UserService:- Within a
@Transactionalmethod, it fetches theUserand theCourseentities from MySQL. - It updates the
user_coursejoin table by modifying the set of courses associated with the user. - Calls
userRepository.save(user)to persist the changes. - Calls
platformSyncService.syncPlatformsByCourses(allAffectedCourses). This is a crucial step: it ensures that all platforms containing the affected courses are updated.
- Within a
-
PlatformSyncService:- Receives the set of affected
Courseentities. - It determines the unique set of
Platforms associated with these courses. - For each unique platform, it calls
syncToMongo(platform), which follows the same logic as in the "Create a Platform" flow, rebuilding and saving thePlatformDocumentin MongoDB.
- Receives the set of affected
This flow shows how a delete operation is propagated to both databases.
DELETE /api/platforms/{id}
-
PlatformController:- Receives the HTTP DELETE request with the platform
id. - Calls
platformService.deletePlatformById(id). - Returns the DTO of the deleted platform.
- Receives the HTTP DELETE request with the platform
-
PlatformService:- Within a
@Transactionalmethod, it fetches thePlatformfrom MySQL to ensure it exists. - Calls
platformRepository.delete(platform). Due toCascadeType.ALLandorphanRemoval=trueon thePlatform-Courserelationship, all courses associated with the platform are also deleted from MySQL. - Calls
platformSyncService.deletePlatformFromMongo(id)to trigger an asynchronous deletion from MongoDB.
- Within a
-
PlatformSyncService:- Receives the platform ID.
- Finds the corresponding document in MongoDB by its ID and deletes it using
platformDocRepository.delete().