This document provides guidelines for developing a Spring Boot project. It includes coding standards, Spring Boot best practices and testing recommendations to follow.
- Java 21 or later
- Docker and Docker Compose
- Maven (or use the included Maven wrapper)
Follow package-by-feature/module and in each module package-by-layer code organization style:
project-root/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/mycompany/projectname/
│ │ │ ├── config/
│ │ │ ├── module1/
│ │ │ │ ├── api/
│ │ │ │ │ ├── controllers/
│ │ │ │ │ └── dtos/
│ │ │ │ ├── config/
│ │ │ │ ├── domain/
│ │ │ │ │ ├── entities/
│ │ │ │ │ ├── exceptions/
│ │ │ │ │ ├── mappers/
│ │ │ │ │ ├── models/
│ │ │ │ │ ├── repositories/
│ │ │ │ │ └── services/
│ │ │ │ ├── jobs/
│ │ │ │ ├── eventhandlers/
│ │ └── resources/
│ │ └── application.properties
│ └── test/
│ │ └── java/
│ │ │ └── com/mycompany/projectname/
│ │ │ ├── module1/
│ │ │ │ ├── api/
│ │ │ │ │ ├── controllers/
│ │ │ │ ├── domain/
│ │ │ │ │ └── services/
└── README.md
-
Web Layer (
com.companyname.projectname.module.api
):- Controllers handle HTTP requests and responses
- DTOs for request/response data
- Global exception handling
-
Service Layer (
com.companyname.projectname.module.domain.services
):- Business logic implementation
- Transaction management
-
Repository Layer (
com.companyname.projectname.module.domain.repositories
):- Spring Data JPA repositories
- Database access
-
Entity Layer (
com.companyname.projectname.module.domain.entities
):- JPA entities representing database tables
-
Model Layer (
com.companyname.projectname.module.domain.models
):- DTOs for domain objects
- Command objects for operations
-
Mapper Layer (
com.companyname.projectname.module.domain.mappers
):- Converters from DTOs to JPA entities and vice-versa
-
Exceptions (
com.companyname.projectname.module.domain.exceptions
):- Custom exceptions
-
Config (
com.companyname.projectname.module.config
):- Spring Boot configuration classes such as WebMvcConfig, WebSecurityConfig, etc.
-
Java Code Style:
- Use Java 21 features where appropriate (records, text blocks, pattern matching, etc.)
- Follow standard Java naming conventions
- Use meaningful variable and method names
- Use
public
access modifier only when necessary
-
Testing Style:
- Use descriptive test method names
- Follow the Given-When-Then pattern
- Use AssertJ for assertions
-
Dependency Injection Style
- Don't use Spring Field Injection in production code.
- Use Constructor Injection without adding
@Autowired
.
-
Transactional Boundaries
- Make a business logic layer (@Service classes) as a transactional boundary.
- Annotate methods that perform DB read-only operations with @Transactional(readOnly=true).
- Annotate methods that perform DB write operations with @Transactional.
- Keep transactions as short as possible.
-
Don't use JPA entities in the "web" layer
- Instead, create dedicated Request/Response objects as Java records.
- Use Jakarta Validation annotations on Request object.
-
Create custom Spring Data JPA methods with meaningful method names using JPQL queries instead of using long derived query method names.
-
Create usecase specific Command objects and pass them to the "service" layer methods to perform create or update operations.
-
Application Configuration:
- Create all the application-specific configuration properties with a common prefix in
application.properties
file. - Use Typed Configuration with
@ConfigurationProperties
with validations.
- Create all the application-specific configuration properties with a common prefix in
-
Implement Global Exception Handling:
@ControllerAdvice
/@RestControllerAdvice
with@ExceptionHandler
methods.- Return consistent error payloads (e.g. a standard
ErrorResponse
DTO).
-
Logging:
- Never use
System.out.println()
for production logging. - Use SLF4J logging.
- Never use
-
Use WebJars for service static content.
-
Don't use Lombok.
Use Flyway for database migrations:
- Migration scripts should be in
src/main/resources/db/migration
- Naming convention:
V{version}__{description}.sql
- Hibernate is configured with
ddl-auto=validate
to ensure schema matches entities
- Unit Tests: Test individual components in isolation using mocks if required
- Integration Tests: Test interactions between components using Testcontainers
- Use descriptive test names that explain what the test is verifying
- Follow the Given-When-Then pattern for a clear test structure
- Use AssertJ for assertions for more readable assertions
- Prefer testing with real dependencies in unit tests as much as possible instead of using mocks
- Use Testcontainers for integration tests to test with real databases, message brokers, etc
- TestcontainersConfiguration.java: Configures database, message broker, etc containers for tests
- BaseIT.java: Base class for integration tests that sets up:
- Spring Boot test context using a random port
- MockMvcTester for HTTP requests
- Import
TestcontainersConfiguration.java
- Min 80% Code Coverage: Aim for good code coverage, but be pragmatic. Don't write useless tests just for the sake of code coverage metrics.
While using JetBrains Junie AI Coding Agent, you can create
.junie/guidelines.md
file with the above content to provide your coding style and preferences to be followed by Junie.