MyWorld project - Yash-777/MyWorld GitHub Wiki
Spring Boot modular project structure for your MyWorld project, following a modular approach with myworld-api, myworld-common, myworld-db, myworld-security, and other modules.
Project Structure & Multi module project using eclipse
π File -> new -> Project -> Maven project
- New Maven Project
select Create a simple project
, which will only create a pom file
and skip all other unnecessary folders for multi module project.
1οΈβ£ Project Name and Workspace Location: C:\Yashwanth\WorkSetup\WorkSpace\myworld
. Here, I have already created a folder named as myworld
where we will create our multi module project which I have selected as workspace location.
2οΈβ£ Configure Project: [Group Id:com.github.yash777
, Artifact Id: myworld
, packaging: pom
]
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.yash777</groupId>
<artifactId>myworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>MyWorld modular projec</name>
<description>Spring Boot modular project structure for your MyWorld project</description>
</project>
Description of Each Module
- myworld-api β Contains API interfaces and DTOs (Data Transfer Objects).
- myworld-common β Contains utility classes, constants, and common logic.
- myworld-db β Manages database entities, repositories, and configurations.
- myworld-security β Handles authentication, authorization, and security logic.
- myworld-service β Implements business logic and interacts with repositories.
- myworld-web β Exposes REST APIs and serves as the entry point for requests.
myworld/
βββ myworld-api/
β βββ src/main/java/com/github/yash777/myworld/api/
β βββ src/main/resources/
β βββ pom.xml
β
βββ myworld-common/
β βββ src/main/java/com/github/yash777/myworld/common/
β βββ src/main/resources/
β βββ pom.xml
β
βββ myworld-db/
β βββ src/main/java/com/github/yash777/myworld/db/
β βββ src/main/resources/
β βββ pom.xml
β
βββ myworld-security/
β βββ src/main/java/com/github/yash777/myworld/security/
β βββ src/main/resources/
β βββ pom.xml
β
βββ myworld-service/
β βββ src/main/java/com/github/yash777/myworld/service/
β βββ src/main/resources/
β βββ pom.xml
β
βββ myworld-web/
β βββ src/main/java/com/github/yash777/myworld/web/
β βββ src/main/resources/
β βββ pom.xml
β
βββ pom.xml (Parent POM)
π We are done with our parent, now letβs add child module. Right click on the project myworld
-> new -> project -> Maven Module
1οΈβ£ Module Name: myworld-api
2οΈβ£ Configure Project: [Group Id:com.github.yash777
, Artifact Id: myworld-api
, packaging: jar
/war
, Name: API Endpoints
]
Here's a clear, structured explanation showing how only the main module's application.properties is automatically loaded by Spring Boot β and how you can manually load submodule-specific configuration files (like log-config.properties) from a child module (myapp-common)
β Multi-Module Maven Project Structure
myapp-parent/
β
βββ myapp-common/ # Shared module with common utilities
β βββ pom.xml
β βββ src/main/java/com/example/common/
β β βββ LogConfig.java
β β βββ LogProperties.java
β βββ src/main/resources/
β βββ application.properties β (NOT auto-loaded)
β βββ log-config.properties β
Custom properties file for logging
β
βββ myapp-application/ # Main application module
β βββ pom.xml
β βββ src/main/java/com/example/app/
β β βββ MyAppApplication.java β
Main class
β βββ src/main/resources/
β βββ application.properties β
Loaded automatically by Spring Boot
β
βββ pom.xml # Parent POM
π§ Spring Boot Behavior (Important!)
β
Only one application.properties
is automatically loaded β the one in the module where the @SpringBootApplication main class
lives (myapp-application).
β application.properties
in myapp-common
will be ignored unless you explicitly load it.
β
If you want to load module-specific configs (like for logging), create a custom-named properties file like log-config.properties
or application-log.properties
.
β Solution: Load Submodule-Specific Config Manually
πΈ log-config.properties
(inside myapp-common/src/main/resources/
):
log.file-path=D:/temp
log.file-name=common-module.log
log.pattern=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
log.max-size-mb=10
log.max-backups=5
log.file-extension=gz
β Use hyphen-case in properties if you're using @ConfigurationProperties
, unless you bind with relaxed naming. Spring Boot will bind log.file-path
to filePath
.
πΈ LogProperties.java
(in myapp-common
)
package com.example.common;
// Loads logging configuration from a custom properties file in the common module.
@lombok.Data @lombok.ToString
@org.springframework.stereotype.Component
@EnableConfigurationProperties(LogProperties.class) // Make sure to enable configuration binding - In your main class or config
@org.springframework.boot.context.properties.ConfigurationProperties(prefix = "log")
@org.springframework.context.annotation.PropertySource("classpath:log-config.properties") // β
Load manually
public class LogProperties {
private String filePath, fileName, pattern;
private long maxSizeMb;
private int maxBackups;
private String fileExtension = "gz"; // default to gz if not set
}
πΈ LogConfig.java
(in myapp-common)
@Configuration @Slf4j
public class LogConfig {
@Autowired
private LogProperties logProperties;
@PostConstruct
public void setupLogger() {
log.info("π Loaded Logging Properties from log-config/application-log.properties: {}", logProperties);
System.out.println("LogProperties: " + logProperties);
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
// Setup the encoder
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext(context);
encoder.setPattern(logProperties.getPattern());
encoder.start();
// Setup the rolling file appender
RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<>();
fileAppender.setContext(context);
fileAppender.setFile(logProperties.getFilePath() + "/" + logProperties.getFileName());
fileAppender.setAppend(true); // β
Ensure it appends to the existing log file
fileAppender.setEncoder(encoder);
// Determine extension
String extension = logProperties.getFileExtension().toLowerCase().startsWith(".")
? logProperties.getFileExtension()
: "." + logProperties.getFileExtension();
// Rolling policy
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
rollingPolicy.setContext(context);
rollingPolicy.setParent(fileAppender);
rollingPolicy.setFileNamePattern(logProperties.getFilePath() + "/" + logProperties.getFileName() + ".%i" + extension); // .gz or .zip
rollingPolicy.setMinIndex(1);
rollingPolicy.setMaxIndex(logProperties.getMaxBackups());
rollingPolicy.start();
// Triggering policy
SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<>();
triggeringPolicy.setContext(context);
triggeringPolicy.setMaxFileSize(new FileSize(logProperties.getMaxSizeMb() * 1024 * 1024)); // Convert MB to bytes
triggeringPolicy.start();
fileAppender.setRollingPolicy(rollingPolicy);
fileAppender.setTriggeringPolicy(triggeringPolicy);
fileAppender.start();
// Register this appender with root logger
Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.addAppender(fileAppender);
}
}
By default, RollingFileAppender
does not append unless you explicitly set:
fileAppender.setAppend(true);
Without this, it overwrites the existing file every time your app starts.
π Sample Flow
1οΈβ£ myworld-api (API Layer)
package com.github.yash777.myworld.api;
public interface UserService {
String getUserInfo(Long userId);
}
2οΈβ£ myworld-common (Utility Layer)
package com.github.yash777.myworld.common;
public class Constants {
public static final String USER_NOT_FOUND = "User not found!";
}
3οΈβ£ myworld-db (Repository Layer)
package com.github.yash777.myworld.db.entity;
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
package com.github.yash777.myworld.db.repository;
import com.github.yash777.myworld.db.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
4οΈβ£ myworld-service (Service Layer)
package com.github.yash777.myworld.service;
import com.github.yash777.myworld.api.UserService;
import com.github.yash777.myworld.db.entity.User;
import com.github.yash777.myworld.db.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class UserServiceImpl implements UserService {
@Autowired private UserRepository userRepository;
@Override
public String getUserInfo(Long userId) {
Optional<User> user = userRepository.findById(userId);
return user.map(User::getName).orElse("User Not Found");
}
}
5οΈβ£ myworld-security (Security Layer)
package com.github.yash777.myworld.security;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.formLogin()
.and()
.httpBasic();
}
}
6οΈβ£ myworld-web (Controller Layer)
package com.github.yash777.myworld.web;
import com.github.yash777.myworld.api.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired private UserService userService;
@GetMapping("/{id}")
public String getUser(@PathVariable Long id) {
return userService.getUserInfo(id);
}
}
π Parent pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.yash777.myworld</groupId>
<artifactId>myworld</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<modules>
<module>myworld-api</module>
<module>myworld-common</module>
<module>myworld-db</module>
<module>myworld-security</module>
<module>myworld-service</module>
<module>myworld-web</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<java.version>17</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
π How to Run the Project Start the Database (if using PostgreSQL, MySQL, etc.).
Build the Project:
mvn clean install
Run the Web Module:
cd myworld-web
mvn spring-boot:run
Test the API in Browser/Postman:
GET http://localhost:8080/users/1