A Comprehensive Spring Boot Project Template and Best Practices for Backend Development
This article presents a complete Spring Boot project template for an e‑commerce order service, covering README composition, one‑click local build scripts, business‑oriented package layout, automated test classification, logging, exception handling, background tasks with distributed locks, static analysis, health checks, Swagger API documentation, database migration, multi‑environment configuration, CORS setup, and a curated list of useful third‑party libraries.
In many software projects the initial setup—basic code scaffolding, CI pipelines, and documentation—is often called the "zero‑iteration" work. After a project runs for a while, developers usually discover missing tests, inconsistent naming, or insufficient architectural decisions. To address these pain points, the author created a reusable Spring Boot project template that encapsulates common best practices for backend development.
README
A good README gives a quick overview, helps newcomers start the project, and reduces communication overhead. It should contain project description, technology stack, local build commands, domain model overview, test strategy, architecture diagrams, deployment diagrams, external dependencies, environment information, coding conventions, and FAQ.
One‑Click Local Build
Three scripts simplify the developer workflow: idea.sh – generates IntelliJ project files and opens the IDE. run.sh – starts the application together with a local MySQL container and enables remote debugging on port 5005. local-build.sh – runs the local build; only after a successful build can code be committed.
#!/usr/bin/env bash
./gradlew clean bootRunDirectory Structure
The project follows the Maven‑style layout (also used by Gradle) and adds a gradle folder for custom scripts. Example tree:
order-backend
├── gradle // Gradle configuration files
├── src // Java source code
├── idea.sh // generate IntelliJ project
├── local-build.sh // local build script
└── run.sh // run application locallyBusiness‑Based Packaging
Instead of technical layers (controller, service, infrastructure), packages are organized around domain concepts such as order and product. Each business package contains its own application service, controller, repository, and model sub‑package, reducing navigation cost and making future micro‑service extraction easier.
order
├── OrderApplicationService.java
├── OrderController.java
├── OrderNotFoundException.java
├── OrderRepository.java
├── OrderService.java
└── model
├── Order.java
├── OrderFactory.java
├── OrderId.java
├── OrderItem.java
└── OrderStatus.javaAutomated Test Classification
Three test types are defined:
Unit tests – core domain models, factories, and domain services.
Component tests – classes that are hard to unit‑test (e.g., repositories, distributed locks).
API tests – end‑to‑end verification of HTTP endpoints.
Gradle source sets separate the code:
sourceSets {
componentTest {
compileClasspath += sourceSets.main.output + sourceSets.test.output
runtimeClasspath += sourceSets.main.output + sourceSets.test.output
}
apiTest {
compileClasspath += sourceSets.main.output + sourceSets.test.output
runtimeClasspath += sourceSets.main.output + sourceSets.test.output
}
}Test directories:
src/test/java – unit tests
src/componentTest/java – component tests
src/apiTest/java – API tests
Logging
Two concerns are highlighted: adding a request ID via MDC and centralising logs with ELK/Redis. Example MDC filter:
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String headerRequestId = request.getHeader(HEADER_X_REQUEST_ID);
MDC.put(REQUEST_ID, isNullOrEmpty(headerRequestId) ? newUuid() : headerRequestId);
try {
filterChain.doFilter(request, response);
} finally {
clearMdc();
}
}Redis appender configuration (used by Logback):
<appender name="REDIS" class="com.cwbase.logback.RedisAppender">
<tags>ecommerce-order-backend-${ACTIVE_PROFILE}</tags>
<host>elk.yourdomain.com</host>
<port>6379</port>
<password>whatever</password>
<key>ecommerce-ordder-log</key>
<mdc>true</mdc>
<type>redis</type>
</appender>Exception Handling
A hierarchical exception model is used. All custom exceptions extend AppException, which carries an ErrorCode enum, HTTP status, message, and additional data.
public abstract class AppException extends RuntimeException {
private final ErrorCode code;
private final Map<String, Object> data = newHashMap();
}Example concrete exception:
public class OrderNotFoundException extends AppException {
public OrderNotFoundException(OrderId orderId) {
super(ErrorCode.ORDER_NOT_FOUND, ImmutableMap.of("orderId", orderId.toString()));
}
}Unified error response structure ( ErrorDetail) is returned to the client.
public final class ErrorDetail {
private final ErrorCode code;
private final int status;
private final String message;
private final String path;
private final Instant timestamp;
private final Map<String, Object> data = newHashMap();
}Background Tasks & Distributed Lock
Spring’s @EnableAsync and @EnableScheduling are enabled. A custom TaskExecutor with MDC support is defined. Distributed locking is provided by ShedLock with a JDBC lock provider.
@Configuration
@EnableAsync
@EnableScheduling
public class SchedulingConfiguration implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(newScheduledThreadPool(10));
}
@Bean(destroyMethod = "shutdown")
@Primary
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(10);
executor.setTaskDecorator(new LogbackMdcTaskDecorator());
executor.initialize();
return executor;
}
} @Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class DistributedLockConfiguration {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
@Bean
public DistributedLockExecutor distributedLockExecutor(LockProvider lockProvider) {
return new DistributedLockExecutor(lockProvider);
}
}Usage example:
public String doBusiness() {
return distributedLockExecutor.executeWithLock(() -> "Hello World.",
new LockConfiguration("key", Instant.now().plusSeconds(60)));
}Unified Code Style
Checkstyle enforces formatting. Additional conventions include using Command suffix for request DTOs, Representation for response DTOs, consistent three‑layer or DDD tactical architecture, unified pagination objects, and shared test base classes.
Static Code Analysis
Gradle plugins used: Checkstyle, SpotBugs, OWASP Dependency‑Check, and SonarQube.
Health Check
A simple endpoint (e.g., /about) returns HTTP 200 with build metadata, request ID, and environment information. Spring Boot Actuator can provide similar functionality.
API Documentation
Swagger (Springfox) automatically generates API docs from controller signatures. Configuration example:
@Configuration
@EnableSwagger2
@Profile({"local", "dev"})
public class SwaggerConfiguration {
@Bean
public Docket api() {
return new Docket(SWAGGER_2)
.select()
.apis(basePackage("com.ecommerce.order"))
.paths(any())
.build();
}
}Database Migration
Flyway manages schema changes. Migration scripts are placed under src/main/resources/db/migration with versioned filenames (e.g., V1__init.sql, V2__create_product_table.sql).
Multi‑Environment Builds
Typical environments: local, ci, dev, qa, uat, prod. Profiles and configuration files are used to adapt the application for each stage.
CORS
For front‑end/back‑end separation, a global CORS mapping is added via WebMvcConfigurer. When Spring Security is present, the CORS configuration must be applied before the security filter chain.
@Configuration
public class CorsConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
}Common Third‑Party Libraries
Guava, Apache Commons, Mockito, DBUnit, Rest‑Assured, Jackson 2, JJWT, Lombok, OpenFeign, Apache Tika, iText, ZXing, XStream, etc.
Conclusion
The article demonstrates a practical, opinionated Spring Boot backend starter that covers everything from project scaffolding to deployment‑ready features. While the presented choices are not the only possible ones, they provide a solid baseline that teams can adapt to their own contexts.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
