This document outlines the coding standards, architectural patterns, and best practices for the Bookstore application.
- React 18 for building user interfaces
- Tailwind CSS for utility-first styling
- Vite for frontend tooling and development environment
- Spring Boot as the application framework
- RESTful APIs for data exchange and communication between client and server
- Spring Data JDBC for persistence (not JPA/Hibernate)
- MySQL as the database system
- TestContainers for integration testing with real database instances
- JUnit 5 for test framework
- DO NOT use mocks - prefer real dependencies or test doubles
- DO NOT use Lombok - write standard Java code instead
- DO NOT use MapStruct - implement mappers manually
- DO NOT use Mockito - prefer integration testing with TestContainers
- DO NOT use H2 - use MySQL instead
com.example.bookstore/
├── config/ # Application configuration
├── controller/ # REST controllers and web controllers
├── dto/ # Data Transfer Objects
├── exception/ # Custom exceptions
├── mapper/ # Manual DTO-Entity mappers
├── model/ # Domain entities
├── repository/ # Data access layer
└── service/ # Business logic
The application follows a strict layered architecture with a clear flow of control:
- Controller Layer: Handles HTTP requests, delegates to services
- Service Layer: Contains business logic, coordinates operations
- Repository Layer: Manages data access and persistence
- Model Layer: Represents domain entities
The flow of control should always follow the pattern: Controllers -> Services -> Repositories. Each layer should only interact with the layer directly below it. Controllers should never directly access Repositories.
- Use
@RestController
for API endpoints - Use
@Controller
for web pages with React - Follow RESTful naming conventions for endpoints:
- Use nouns instead of verbs (e.g.,
/books
instead of/getBooks
) - Use plural nouns for collections (e.g.,
/books
instead of/book
) - Use HTTP methods appropriately:
- GET for retrieving resources
- POST for creating resources
- PUT for updating resources
- DELETE for removing resources
- Use hierarchical relationships (e.g.,
/books/{id}/authors
)
- Use nouns instead of verbs (e.g.,
- Return appropriate HTTP status codes:
- 200 OK for successful GET, PUT, PATCH
- 201 Created for successful POST
- 204 No Content for successful DELETE
- 400 Bad Request for client errors
- 404 Not Found for missing resources
- 500 Internal Server Error for server errors
- Validate input using Bean Validation (
@Valid
) - Implement proper error handling with descriptive error messages
- Implement business logic in service classes
- Use interfaces for services when appropriate
- Handle transactions at the service level with
@Transactional
- Use Spring Data JDBC repositories
- Define custom queries with
@Query
when needed - Keep repository methods focused on data access
- Use DTOs for API requests and responses
- Implement manual mapping between DTOs and entities
- Keep DTOs immutable when possible
- Anemic Domain Model: Ensure entities contain behavior, not just data
- N+1 Query Problem: Be mindful of query performance with relationships
- Excessive Layering: Don't create unnecessary abstraction layers
- Tight Coupling: Use dependency injection to maintain loose coupling
- Inconsistent Error Handling: Use a global exception handler
- Lack of Validation: Always validate input data
- Ignoring Transactions: Be explicit about transaction boundaries
- Classes: PascalCase, descriptive nouns (e.g.,
BookController
) - Methods: camelCase, verb phrases (e.g.,
findAllBooks()
) - Variables: camelCase, descriptive (e.g.,
availableBooks
) - Constants: UPPER_SNAKE_CASE (e.g.,
MAX_BOOK_LIMIT
)
- Use 4 spaces for indentation
- Maximum line length of 120 characters
- Group related methods together
- Order class members: constants, fields, constructors, methods
- Prefer constructor injection over field injection
- Use Java's built-in functional interfaces when appropriate
- Document public APIs with Javadoc
- Include purpose, parameters, return values, and exceptions
- Keep comments up-to-date with code changes
- Use meaningful commit messages
- Write unit tests for services and utilities
- Write integration tests for repositories using TestContainers
- Write end-to-end tests for controllers
- Test both happy paths and edge cases
- Use descriptive test method names (e.g.,
shouldReturnAllBooksWhenFindAllIsCalled
) - Prefer real dependencies over mocks
- Use appropriate assertions for validation
- Use meaningful table and column names
- Define appropriate indexes for performance
- Use migrations for schema changes
- Keep database interactions efficient
- Use appropriate data types