Java Development Standards
Java Language and Coding Style
- Java Version: Projects should use the latest LTS version of the JDK whenever possible.
- Naming Conventions:
- Classes:
PascalCase(e.g.,UserService,OrderController). - Methods:
camelCase(e.g.,getUserById,saveOrder). - Variables:
camelCase(e.g.,username,statusCode). - Constants:
SCREAMING_SNAKE_CASE(e.g.,DEFAULT_PAGE_SIZE).
- Classes:
- Immutability: Prefer immutability for domain objects and DTOs where possible, using
recordsor immutable classes to reduce side effects and improve thread safety. - Optional: Use
Optional<T>to explicitly handle potentially absent values and avoidNullPointerException. - Streams API: Prefer the Java Streams API for collection processing, promoting functional and declarative programming.
- Exception Handling:
- Use specific exceptions. Avoid catching generic
Exception. - Throw runtime exceptions for unrecoverable errors.
- Define custom checked exceptions for recoverable errors if required by business logic.
- Leverage Spring's
@ControllerAdviceand@ExceptionHandlerfor centralised global exception handling and consistent API error responses.
- Use specific exceptions. Avoid catching generic
- Code Review: All backend code must undergo thorough manual code review before merging, particularly following GitFlow principles. IntelliJ IDEA's integrated code analysis tools should be used as a first-pass review.
Documentation and Comments
- All public classes, methods, and significant fields in backend Java code must include comprehensive Javadoc comments.
- Javadoc should explain the purpose, parameters (
@param), return values (@return), and thrown exceptions (@throws). - Javadoc formatting:
-
Javadoc must follow a maximum of 100 characters per line (including whitespace for formatting). If the content exceeds 100 characters, break at the last word that ends within the 100-character limit. However, if after the line break only a single word remains at the start of the next line, break one word earlier.
-
Use
<p>to separate paragraphs. -
Each paragraph must be a grammatically correct and semantically complete paragraph following English sentence conventions.
-
All
@param,@return,@throws, and@seeexplanations must follow these rules:- Do not start with a capital letter.
- If the description ends with a declarative sentence, do not use punctuation at the end.
-
Dependency Management (Gradle)
- Build File:
build.gradle.ktsmust be well-organised with clear separation of plugins, dependencies, and tasks. - Dependency Versions: Dependency versions must be centrally managed in the
gradle/libs.versions.tomlfile to ensure consistency. - Plugin Management: Explicitly declare Gradle plugins and their versions.
- Avoid Unnecessary Dependencies: Only include dependencies actually used by the project. Regularly review and clean up unused dependencies.
API Design (RESTful)
- RESTful Principles: Follow RESTful principles:
- Resources: Model data as resources identifiable by URI.
- HTTP Methods: Use standard HTTP methods appropriately (GET for retrieval, POST for creation, PUT for full update, PATCH for partial update, DELETE for removal).
- Statelessness: APIs should be stateless; each request from client to server must contain all the information needed to understand the request.
- URIs:
- Use plural nouns for collection resources (e.g.,
/users,/products). - Use hyphens in URIs for readability (e.g.,
/user-accounts). - Avoid verbs in URIs (e.g., use
/usersinstead of/getAllUsers).
- Use plural nouns for collection resources (e.g.,
- Status Codes: Use appropriate HTTP status codes to indicate the result of API requests (e.g.,
200 OK,201 Created,204 No Content,400 Bad Request,401 Unauthorized,403 Forbidden,404 Not Found,500 Internal Server Error). - Response Format: JSON is the preferred response format.
- Versioning: Where version control is required, use the
X-Endpoint-Versionheader parameter to control API versions.
Spring Boot Best Practices & Layered Architecture
-
Layered Architecture (MVC with Manager Layer): Our backend applications follow a strict multi-layered architecture, ensuring clear separation of responsibilities and improving maintainability and testability. The layers and their responsibilities are:
- Controller Layer: Located in the
controllerpackage. Responsible for exposing RESTful APIs, handling HTTP requests, and mapping request parameters/bodies to service layer calls. Controllers should remain lightweight, focusing primarily on input validation (using DTOs) and coordinating calls to theServicelayer. - Service Layer: Located in the
servicepackage. This layer encapsulates core business logic. Services expose a directly consumable API to theControllerlayer, abstracting business processes and transaction management. Services coordinate calls to theManagerlayer to execute business operations. - Manager Layer: Located in the
managerpackage. This layer provides atomic business operations that can be composed by theServicelayer. Managers typically handle more complex business logic and may involve interacting with multiple repositories or other external systems at a finer granularity. - Repository Layer (MyBatis): Located in the
repositorypackage andsrc/main/resources/repository(for XML mapping files). This layer is responsible for providing atomic database interaction operations.
Inter-Layer Communication Strategy:
-
Components within a layer may only call components in the layer directly below it.
-
Cross-layer calls are strictly prohibited (e.g., Controller directly calling Manager, Service directly calling Repository).
-
Upward calls are strictly prohibited (e.g., Service calling Controller).
-
Lateral calls (e.g., Service A calling Service B for a different domain) should be carefully considered and typically indicate a need to refactor shared logic into a
Manageror a dedicatedServicefor that shared concern. -
Configuration: Prefer
application.ymloverapplication.propertiesfor configuration properties, offering better readability and hierarchical structure. Use@ConfigurationPropertiesfor type-safe configuration. -
Dependency Injection: All dependencies should use constructor injection (mandatory dependencies) or setter injection (optional dependencies). Avoid
@Autowiredon fields, as it makes testing more difficult and hides dependencies. -
Services: Business logic classes are annotated with
@Service. Services should be kept lean and focused on orchestrating domain operations, typically involving business logic processing throughManagerlayer components and interaction throughManageror directly with MyBatis repositories (if the particular operation does not require intermediate Manager logic, though the Manager layer is preferred for all repository interactions consistent with the layered architecture definition).
- Controller Layer: Located in the
-
Repositories (MyBatis & JPA):
- The project uses both MyBatis and JPA for database operations. Repository interfaces in the
repositoryandmapperpackages define the data access contract. - Corresponding SQL definitions are managed in XML mapping files located in
src/main/resources/mapper. - Data operation conventions:
- Use JPA for simple database operations.
- Use MyBatis for complex database operations.
- When performing paginated queries, page numbers should start from 0 (compatible with Spring Data JPA).
- Data Access Method Naming Conventions:
- For querying data lists: methods must start with
selectListBy, followed by the filter criteria (e.g.,selectListByUserId,selectListByDepartmentIdAndStatus). These methods must also include aPageRequestparameter for pagination. - For querying single data records: methods must start with
selectOne(e.g.,selectOneById,selectOneByUsername). - For saving new data: methods must be named
save, and Mapper method return type must beint(affected row count). - For updating existing data: methods must be named
update, and Mapper method return type must beint(affected row count). - For deleting data: methods must start with
deleteBy, clearly indicating the deletion criteria (e.g.,deleteById). Mapper method return type must beint(affected row count).
- For querying data lists: methods must start with
- The project uses both MyBatis and JPA for database operations. Repository interfaces in the
-
Controllers:
- Annotate REST controllers with
@RestController. - Use
@GetMapping,@PostMapping, etc. to map HTTP methods (GET, POST, PUT, DELETE) to appropriate controller methods. - Ensure request and response payloads are clearly defined (DTOs) and documented.
- Controllers should primarily handle HTTP request/response mapping and delegate actual business logic to the service layer.
- Annotate REST controllers with
-
DTOs (Data Transfer Objects): Define separate DTOs for request and response bodies to decouple the internal domain model from the API contract. Use validation annotations on DTOs (e.g.,
@Valid,@NotNull,@Size). -
Logging (SLF4J & Logback):
- All logging in the application uses
org.slf4j.Logger. - Configure
application-dev.ymlto set logging levels, appenders, and output formats. - Log messages should be descriptive and provide sufficient context. Avoid logging sensitive information.
- Use parameterised logging for performance and to prevent string concatenation overhead (e.g.,
log.debug("Processing user: {}", userId);). - Standard log levels:
ERROR,WARN,INFO,DEBUG,TRACE.
- All logging in the application uses
Project Structure
Backend applications follow a structured Gradle project layout. The core application should be clearly divided into various sub-packages.
Key Observations and Specific Instructions:
- External
configDirectory- Environment configurations (
application-dev.yml,application-prod.yml.example) are managed in the top-levelconfigdirectory, separate fromsrc/main/resources. This facilitates environment-specific property management, allowing different configurations to be mounted or linked at deployment time. - Do not upload any configuration files other than
src/main/resources/application.ymlto the Git repository.
- Environment configurations (
- Database Initialisation: The
database/init.ddirectory is reserved for SQL scripts, specifically database schema initialisation (init-en_GB.sql), which is critical for environment setup and CI/CD pipelines. This structure suggests a "schema-first" or "code-driven schema evolution" approach. clientPackage: This package is used to provide services for all middleware to the application, such as HTTP, S3 storage, Redis calls, etc. For self-coded functional implementations that need to be added to the Spring context (such as JSON Web Token generation and parsing), placing them in this package is also recommended.- MyBatis & Spring Data JPA Integration
- The
src/main/java/.../mapperpackage contains MyBatis mapper interfaces, while the actual SQL definitions reside insrc/main/resources/mapper/*.xmlfiles. This separation is key to keeping code clean while leveraging MyBatis's powerful XML mapping capabilities. - The
src/main/java/.../repositorypackage contains Spring Data JPA interfaces.
- The
domainPackage Granularity:domain.entity: Reserved for classes directly mapped to database tables (POJOs for MyBatis).domain.model: For more general-purpose domain objects or aggregate roots that do not map one-to-one with individual tables.domain.view: Specifically for Data Transfer Objects (DTOs) used in read-only scenarios (e.g., query results, report structures).domain.web.request/domain.web.response: Clearly separated DTOs for incoming API requests and outgoing API responses, strictly adhering to the API contract and decoupled from internal domain entities.
managerandprocessorPackages: These packages imply a layered architecture where "managers" coordinate operations involving multiple services or repositories, while "processors" may handle specific aspects of business processes. It is mandatory to clearly define the responsibilities of classes in these packages to prevent anti-patterns such as "anaemic domain model" or "god objects".securityPackage: This sub-package contains custom Spring Security components beyond the initial configuration, such as custom authentication types and providers, indicating a tailored security implementation.propertiesPackage: This package is for custom@ConfigurationPropertiesclasses, facilitating type-safe access to application settings defined in YML files — well-positioned.extensionPackage: This is a flexible area for application-specific extensions, such as custom Jackson serialisers or Redis customisations. It should be used sparingly to avoid becoming a "miscellaneous" dumping ground.
Security (Spring Security)
- Mandatory Use: Spring Security is mandatory in all Spring Boot web applications.
- Authentication & Authorisation: Configure authentication mechanisms (e.g., OAuth 2.0, JWT, session-based) and authorisation rules in
SecurityConfig.java. - CSRF Protection: Ensure CSRF protection is enabled for state-modifying operations, unless there is a strong reason to disable it (e.g., stateless APIs with other mechanisms already in place).
- CORS: Correctly configure Cross-Origin Resource Sharing (CORS) according to frontend deployment requirements.
- Third-Party Identity Providers: When integrating with third-party identity providers (e.g., Microsoft Entra ID), follow best practices for secure token handling and user provisioning. Sensitive credentials must be securely managed (e.g., environment variables, Vault).
- Input Validation: Always validate all user input on the server side to prevent common vulnerabilities such as SQL injection, XSS, etc.
- Content Security Policy (CSP): Consider implementing a robust CSP for the frontend to mitigate XSS attacks.