Backend

Functional Requirements

  • Functional Requirements is covered by the attached Product Requirements Document.
  • Implement RESTful APIs for all functionalities based on the attached OpenAPI specification.
  • Use Spring Data REST for API implementation wherever applicable.

Build system

  • Maven
  • Try not to hard code dependencies versions, use versions from BOMs wherever possible
  • If a dependency is available in Spring Boot BOM, use the version from there, if not, use the latest stable version
  • For explicit version declarations, use properties in POM

Backend Tech Stack

  • Java 21
  • Spring Boot
  • Spring Boot Actuator
  • Spring Security OAuth2 Resource Server
  • Spring Security LDAP
    • org.springframework.ldap:spring-ldap-core
    • org.springframework.security:spring-security-ldap
    • com.unboundid:unboundid-ldapsdk
  • Spring Web
  • Spring REST Docs (spring-boot-starter-data-rest)
  • Spring HATEOAS
  • Spring Modulith
    • org.springframework.modulith:spring-modulith-starter-core
    • org.springframework.modulith:spring-modulith-events-api
    • org.springframework.modulith:spring-modulith-events-kafka
    • org.springframework.modulith:spring-modulith-starter-test
  • Spring for Apache Kafka
    • org.springframework.kafka:spring-kafka
    • org.springframework.kafka:spring-kafka-test
  • Springdoc OpenAPI
    • springdoc-openapi-starter-webmvc-api
    • springdoc-openapi-starter-webmvc-ui
  • Spring Data JPA
  • Flyway
    • org.flywaydb:flyway-database-postgresql
  • Lombok
  • MapStruct
  • Spring Boot Test
    • org.springframework.boot:spring-boot-starter-test
  • JUnit 5
  • Testcontainers

Database

  • PostgreSQL
  • Use UUID as primary key type
  • Use PostgreSQL enums wherever applicable
  • Flyway for database migration
  • Create DDL scripts for initial schema setup and use Flyway for subsequent migrations
  • Create DML scripts for inserting sample data and use Flyway for subsequent migrations

Code organization

  • Root package name: cq.playground

REST API

  • Follow RESTful principles and Hypermedia as the engine of application state (HATEOAS)
  • Implement proper exception handling and return appropriate HTTP status codes
  • Use plural nouns for resource names in endpoints (e.g., /users, /orders)
  • HTTP 200 for successful GET, PUT, PATCH requests
  • HTTP 201 for successful POST requests
  • HTTP 204 for successful DELETE requests with no content
  • HTTP 400 for bad requests on client side errors
  • HTTP 404 for missing resources
  • HTTP 500 for server side errors
  • Version the API using custom HTTP header X-API-Version instead of URL path

Java

  • Use var for local variable declaration.
  • Prefer method references over lambda expressions wherever applicable.
  • Use capitalized names for constants and enums.

DTO Classes

  • DTOs should be immutable by using Lombok @Value annotation
  • Use DTOs for all data transfer between client and server
  • Make all fields final in DTO classes
  • Add @AllArgsConstructor to DTO classes
  • Use MapStruct for transformation between entities and DTOs

Spring Web

  • Define a global handler class annotated with @ControllerAdvice (or @RestControllerAdvice for REST APIs) using @ExceptionHandler methods to handle specific exceptions.
  • Return consistent error responses. Consider using the ProblemDetails response format (RFC 9457 (opens in a new tab)).
  • Use @Valid annotation on controller method parameters to trigger validation based on Jakarta Validation annotations in DTOs.

Persistence

Spring Data JPA

  • Use UUID as primary key type
  • Use @ManyToOne, @OneToMany, @OneToOne, and @ManyToMany annotations to define relationships between entities
  • Use FetchType.LAZY for all relationships unless there is a compelling reason to use EAGER fetching
  • Avoid bidirectional relationships unless necessary to prevent complexity and potential performance issues
  • Disable Hibernate's automatic schema generation (e.g., spring.jpa.hibernate.ddl-auto=none) and use Flyway for database migrations instead
  • Disable Open Session in View pattern by setting spring.jpa.open-in-view=false
  • Use @Transactional annotation on service layer methods that modify data
  • Use pagination for endpoints that return large lists of data
  • Use @Modifying and @Query annotations for custom update and delete operations in repositories
  • For JPA entities, use @JdbcType(PostgreSQLEnumJdbcType.class) for mapping PostgreSQL enums
  • Use Spring Data JPA repositories for data access
  • Define custom query methods using @Query in repository interfaces as needed

JPA/Hibernate

  • Enable performance optimizations like second-level cache, query cache, and batch fetching

Configuration

Spring Framework

  • Use constructor injection for Spring beans
  • DO NOT use public visibility for Spring beans, use package-private (default) visibility instead, except for @Service, @Component, and @Repository classes

Spring Boot

  • Use the latest stable version of Spring Boot
  • Turn off console banner

Security

Spring Security

  • (MUST) Sanitize all user inputs to prevent XSS attacks.
  • (MUST) Implement Content Security Policy (CSP) headers.
  • (MUST) Use HTTPS everywhere, implement proper CORS policies.
  • (SHOULD) Implement proper authentication and authorization patterns.
  • (MUST) Never expose sensitive data in client-side code or logs.
  • Secure all endpoints except /health, /info, /metrics, /swagger-ui/**, /v3/api-docs/** and /public/**
  • Use method-level security with @PreAuthorize annotations to enforce role-based access control
  • On Controller endpoints, verify user authorization with JWT claims wherever applicable

Spring Security OAuth2 Resource Server

  • Validate JWT tokens using the public key from the authorization server
  • Configure token validation settings in application.yml or application.properties

Spring Security LDAP

  • Use LDAP for user management and identity provider
  • Configure LDAP settings in application.yml or application.properties
  • Use LdapAuthoritiesPopulator to map LDAP groups to application roles
  • Set up an embedded LDAP server for development and testing using UnboundID
  • Create a sample LDIF file to bootstrap LDAP server with users and groups, located in src/main/resources/id.ldif

Secure coding

Messaging

Spring for Apache Kafka

  • Use Kafka for asynchronous messaging between microservices
  • Configure Kafka settings in application.yml or application.properties
  • Use KafkaTemplate for producing messages
  • Use @KafkaListener annotation for consuming messages
  • Implement error handling and retries for message processing
  • Use Avro for message serialization and deserialization
  • Use Schema Registry for managing Avro schemas
  • Use embedded Kafka broker from Spring for Apache Kafka for development and testing

Observability

Observability - Metrics

  • Use Micrometer for metrics collection, with Prometheus as the metrics backend

Observability - Tracing

  • Use Micrometer Observation for application tracing
  • Use datasource-micrometer-spring-boot for JDBC tracing
  • Use OpenTelemetry for distributed tracing

Observability - Logging

  • DO NOT use System.out.println() for application logging
  • Use SLF4J for application logging with Logback as the logging implementation
  • Avoid logging sensitive information or redact sensitive data before logging
  • Guard expensive log calls with conditional checks (e.g., if (log.isDebugEnabled()) { ... })
  • Enable log rotation and archiving to manage log file sizes
  • Enable SQL statement logging by setting logging.level.sql=DEBUG and logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
  • Enable Web HTTP request logging by setting logging.level.web=DEBUG and logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG

Observability - Monitoring

  • Use Grafana for observability visualization

Spring Boot Actuator

  • Expose only essential actuator endpoints (such as /health, /info, /metrics) without requiring authentication. All the other actuator endpoints must be secured.
  • Expose all actuator endpoints for local profile

Coding style

  • Use the following Lombok annotations wherever applicable: @Data, @Builder, @NoArgsConstructor, but don't use @Data on JPA entities
  • Use @EqualsAndHashCode for equals and hashCode methods
  • Use @Slf4j in all classes to enable logging with SLF4J
  • For constructors or methods with more than 3 arguments, use a new line for every argument
  • Use @RequiredArgsConstructor to help constructor injection for Spring beans,
  • Use Objects.toString to convert raw values if String is needed
  • Externalizes all user-facing text such as labels, prompts, and messages into ResourceBundles rather than embedding them in code.

Testing

  • Use AssertJ for assertions and matchers
  • DO NOT use mocks in test cases, and use Testcontainers for integration testing, don't use H2 or any embedded DB
  • Use @SneakyThrows for methods that throw checked exceptions
  • Use @ParameterizedTest for parameterized tests
  • Use Spring REST Docs for API documentation and testing