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.

Java Captain
Java Captain
Java Captain
A Comprehensive Spring Boot Project Template and Best Practices for Backend Development

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 bootRun

Directory 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 locally

Business‑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.java

Automated 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

MicroservicestestingBackend DevelopmentSpring BootProject Template
Java Captain
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.