# Spring Boot Rules

Full coding rules for this stack. Read this in full before writing or modifying any Java code in this project — not just once, keep applying it to every edit in the session, not only the first.

---

## Architecture

Follow strict **Layered Architecture**:

```
Controller (HTTP layer)
    ↓
Service (Business logic)
    ↓
Repository (Data access - Spring Data JPA)
    ↓
Entity / Domain Model
```

### Package Structure

```
com.company.project/
├── controller/         # \@RestController — HTTP endpoints only
├── service/
│   ├── impl/           # \@Service — business logic implementation
│   └── [Interface].java
├── repository/         # \@Repository — extends JpaRepository
├── entity/             # \@Entity — JPA entities
├── dto/
│   ├── request/        # Input DTOs (e.g. CreateUserRequest)
│   └── response/       # Output DTOs (e.g. UserResponse)
├── mapper/             # MapStruct mappers (Entity ↔ DTO)
├── exception/
│   ├── GlobalExceptionHandler.java  # \@RestControllerAdvice
│   └── [CustomException].java
├── config/             # \@Configuration classes
└── util/               # Pure utility helpers (stateless)
```

---

## Controller Rules

- Annotate with `\@RestController` + `\@RequestMapping`
- **Only** handle HTTP concerns: parse request, call service, return response
- Never put business logic in Controller
- Always use DTOs — never expose Entity directly
- Use `ResponseEntity<T>` for explicit HTTP status control
- Validate input with `\@Valid` + Bean Validation annotations

---

## Service Rules

- Always define an **interface**, implement in the `impl/` package
- Annotate implementation with `\@Service`
- All business logic lives here
- Use `\@Transactional` at the method level (not class level)
- Throw specific custom exceptions, not generic `RuntimeException`
- Never return an Entity — always convert to DTO via Mapper

---

## Repository Rules

- Extend `JpaRepository<Entity, ID>`
- Use **method name queries** for simple queries
- Use `\@Query` (JPQL) for complex queries — avoid native SQL unless necessary
- Never add business logic here
- Use `\@EntityGraph` to solve N+1 problems

---

## Entity Rules

- Use Lombok: `\@Getter`, `\@Setter`, `\@NoArgsConstructor`, `\@AllArgsConstructor`, `\@Builder`
- Avoid `\@Data` on entities (causes issues with `equals/hashCode` + lazy loading)
- Always use `\@Table(name = "snake_case_table_name")`
- Use `\@Column(name = "snake_case_column_name")` explicitly
- For soft delete: add `deleted` boolean + `deletedAt` timestamp
- Extend `BaseEntity` for audit fields (`createdAt`, `updatedAt`)

---

## DTO Rules

- Use separate DTOs for **Request** and **Response** — never share
- Use Lombok: `\@Getter`, `\@Builder`, `\@AllArgsConstructor`, `\@NoArgsConstructor`
- Validate in the Request DTO with Bean Validation (`\@NotBlank`, `\@Email`, `\@NotNull`, `\@Size`)
- Never expose internal fields (password hash, audit timestamps) in the Response DTO

---

## MapStruct Mapper Rules

- Use `\@Mapper(componentModel = "spring")` — inject as a Spring bean
- Define explicit mappings with `\@Mapping` when field names differ
- Never do manual mapping (`new DTO(); dto.setField(entity.getField())`)

---

## Exception Handling

- Create custom exceptions extending `RuntimeException`
- Handle all exceptions in one `\@RestControllerAdvice` class
- Return a consistent error response format
- Never expose stack traces to the client

---

## Testing Rules

### Unit Tests (Service layer)
- Test class: `[ServiceImpl]Test.java`
- Mock all dependencies with `\@ExtendWith(MockitoExtension.class)` + `\@Mock`
- Test happy path + edge cases + exception scenarios
- Use AssertJ: `assertThat(result).isEqualTo(expected)`

### Integration Tests (Controller layer)
- Use `\@SpringBootTest` + `\@AutoConfigureMockMvc`
- Test full HTTP flow with `MockMvc`
- Use `\@Sql` or Testcontainers for database state

---

## Naming Conventions

| Element | Convention | Example |
|---------|-----------|---------|
| Class | PascalCase | `UserService`, `OrderController` |
| Method | camelCase | `findById`, `createOrder` |
| Variable | camelCase | `userResponse`, `orderId` |
| Constant | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
| Package | lowercase | `com.company.project.service` |
| DB Table | snake_case | `user_orders`, `product_items` |
| DB Column | snake_case | `created_at`, `user_id` |
| REST endpoint | kebab-case | `/api/v1/user-profiles` |
| Request DTO | `[Action][Resource]Request` | `CreateOrderRequest` |
| Response DTO | `[Resource]Response` | `OrderResponse` |

---

## API Design

- Version all APIs: `/api/v1/...`
- Use plural nouns for resources: `/users`, `/orders`
- HTTP methods: `GET` (read), `POST` (create), `PUT` (full update), `PATCH` (partial), `DELETE`
- Return `201 Created` for POST, `200 OK` for GET/PUT/PATCH, `204 No Content` for DELETE
- Use consistent pagination: `?page=0&size=20&sort=createdAt,desc`

```
GET    /api/v1/users              → 200 list
POST   /api/v1/users              → 201 created
GET    /api/v1/users/{id}         → 200 or 404
PUT    /api/v1/users/{id}         → 200 or 404
DELETE /api/v1/users/{id}         → 204 or 404
GET    /api/v1/users/{id}/orders  → 200 nested resource
```

---

## Performance Rules

- Always use pagination — never return unbounded lists
- Avoid N+1: use `\@EntityGraph` or `JOIN FETCH` in JPQL
- Add database indexes on frequently queried columns
- Use `\@Transactional(readOnly = true)` on read-only service methods
- For heavy read operations, consider projection interfaces

---

## Security Rules

- Never log passwords, tokens, or sensitive PII
- Hash passwords with BCrypt: `passwordEncoder.encode(rawPassword)`
- Validate and sanitize all user inputs via Bean Validation
- Use `\@PreAuthorize` for method-level security
- Never return stack traces to API consumers
- Store secrets in environment variables / Vault — never in code

---

## Logging Rules

- Use SLF4J with Lombok `\@Slf4j`
- `log.info` — normal business events
- `log.warn` — recoverable issues (not found, validation fail)
- `log.error` — unexpected exceptions (always include `ex` as the second argument)
- Never log sensitive data (password, credit card, token)

---

## Common Anti-Patterns to Avoid

- ❌ `\@Autowired` field injection → use constructor injection (Lombok `\@RequiredArgsConstructor`)
- ❌ `\@Data` on JPA entities → use `\@Getter \@Setter` separately
- ❌ Returning `Entity` directly from Controller → always use DTO
- ❌ `SELECT *` or unbounded `findAll()` → always paginate
- ❌ Business logic in Controller → move to Service
- ❌ Catching and swallowing exceptions → handle properly or rethrow
- ❌ `new RuntimeException("something")` → create a specific custom exception
- ❌ Hardcoding config values → use `\@Value` or `\@ConfigurationProperties`
- ❌ `\@Transactional` on Controller → only on Service methods

---

When explaining changes, refer to the [Spring Boot Official Documentation](https://docs.spring.io/spring-boot/docs/current/reference/html/) and [Spring Data JPA](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/) conventions.
