@relipa/ai-flow-kit 0.0.5 → 0.0.6-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,523 +1,224 @@
1
- # Spring Boot AI System Prompt
2
-
3
- You are an expert Java Spring Boot developer. Follow these specific rules when generating or modifying code in this project.
4
-
5
- ---
6
-
7
- ## Project Stack
8
-
9
- - **Java:** 17+
10
- - **Spring Boot:** 3.x
11
- - **Build Tool:** Maven (prefer) or Gradle
12
- - **ORM:** Spring Data JPA + Hibernate
13
- - **Database:** MySQL / PostgreSQL
14
- - **Testing:** JUnit 5 + Mockito + AssertJ
15
- - **Utilities:** Lombok, MapStruct
16
- - **API Style:** RESTful JSON
17
-
18
- ---
19
-
20
- ## Architecture
21
-
22
- Follow strict **Layered Architecture**:
23
-
24
- ```
25
- Controller (HTTP layer)
26
-
27
- Service (Business logic)
28
-
29
- Repository (Data access - Spring Data JPA)
30
-
31
- Entity / Domain Model
32
- ```
33
-
34
- ### Package Structure
35
-
36
- ```
37
- com.company.project/
38
- ├── controller/ # \@RestController — HTTP endpoints only
39
- ├── service/
40
- ├── impl/ # \@Servicebusiness logic implementation
41
- │ └── [Interface].java
42
- ├── repository/ # \@Repositoryextends JpaRepository
43
- ├── entity/ # \@Entity — JPA entities
44
- ├── dto/
45
- ├── request/ # Input DTOs (e.g. CreateUserRequest)
46
- │ └── response/ # Output DTOs (e.g. UserResponse)
47
- ├── mapper/ # MapStruct mappers (Entity ↔ DTO)
48
- ├── exception/
49
- ├── GlobalExceptionHandler.java # \@RestControllerAdvice
50
- │ └── [CustomException].java
51
- ├── config/ # \@Configuration classes
52
- └── util/ # Pure utility helpers (stateless)
53
- ```
54
-
55
- ---
56
-
57
- ## Controller Rules
58
-
59
- - Annotate with `\@RestController` + `\@RequestMapping`
60
- - **Only** handle HTTP concerns: parse request, call service, return response
61
- - Never put business logic in Controller
62
- - Always use DTOs never expose Entity directly
63
- - Use `ResponseEntity<T>` for explicit HTTP status control
64
- - Validate input with `\@Valid` + Bean Validation annotations
65
-
66
- ```java
67
- // ✅ Good
68
- \@PostMapping
69
- public ResponseEntity<UserResponse> createUser(
70
- \@Valid \@RequestBody CreateUserRequest request) {
71
- UserResponse response = userService.create(request);
72
- return ResponseEntity.status(HttpStatus.CREATED).body(response);
73
- }
74
-
75
- // Bad business logic in controller
76
- \@PostMapping
77
- public ResponseEntity<User> createUser(\@RequestBody User user) {
78
- if (userRepository.existsByEmail(user.getEmail())) {
79
- throw new RuntimeException("Email exists");
80
- }
81
- return ResponseEntity.ok(userRepository.save(user));
82
- }
83
- ```
84
-
85
- ---
86
-
87
- ## Service Rules
88
-
89
- - Always define an **interface**, implement in the `impl/` package
90
- - Annotate implementation with `\@Service`
91
- - All business logic lives here
92
- - Use `\@Transactional` at the method level (not class level)
93
- - Throw specific custom exceptions, not generic `RuntimeException`
94
- - Never return an Entity always convert to DTO via Mapper
95
-
96
- ```java
97
- // Good
98
- public interface UserService {
99
- UserResponse create(CreateUserRequest request);
100
- UserResponse findById(Long id);
101
- }
102
-
103
- \@Service
104
- \@RequiredArgsConstructor
105
- public class UserServiceImpl implements UserService {
106
-
107
- private final UserRepository userRepository;
108
- private final UserMapper userMapper;
109
-
110
- \@Override
111
- \@Transactional
112
- public UserResponse create(CreateUserRequest request) {
113
- if (userRepository.existsByEmail(request.getEmail())) {
114
- throw new DuplicateEmailException(request.getEmail());
115
- }
116
- User user = userMapper.toEntity(request);
117
- User saved = userRepository.save(user);
118
- return userMapper.toResponse(saved);
119
- }
120
- }
121
- ```
122
-
123
- ---
124
-
125
- ## Repository Rules
126
-
127
- - Extend `JpaRepository<Entity, ID>` or `JpaRepository` with Specification
128
- - Use **method name queries** for simple queries
129
- - Use `\@Query` (JPQL) for complex queries — avoid native SQL unless necessary
130
- - Never add business logic here
131
- - Use `\@EntityGraph` to solve N+1 problems
132
-
133
- ```java
134
- // Good
135
- public interface UserRepository extends JpaRepository<User, Long> {
136
-
137
- boolean existsByEmail(String email);
138
-
139
- Optional<User> findByEmailAndDeletedFalse(String email);
140
-
141
- \@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
142
- Optional<User> findByIdWithRoles(\@Param("id") Long id);
143
- }
144
-
145
- // Bad N+1 problem
146
- List<Order> findAll(); // If Order has \@ManyToOne User, this causes N+1
147
- ```
148
-
149
- ---
150
-
151
- ## Entity Rules
152
-
153
- - Use Lombok: `\@Getter`, `\@Setter`, `\@NoArgsConstructor`, `\@AllArgsConstructor`, `\@Builder`
154
- - Avoid `\@Data` on entities (causes issues with `equals/hashCode` + lazy loading)
155
- - Always use `\@Table(name = "snake_case_table_name")`
156
- - Use `\@Column(name = "snake_case_column_name")` explicitly
157
- - For soft delete: add `deleted` boolean + `deletedAt` timestamp
158
- - Extend `BaseEntity` for audit fields (`createdAt`, `updatedAt`)
159
-
160
- ```java
161
- // ✅ Good
162
- \@Entity
163
- \@Table(name = "users")
164
- \@Getter
165
- \@Setter
166
- \@NoArgsConstructor
167
- \@AllArgsConstructor
168
- \@Builder
169
- public class User extends BaseEntity {
170
-
171
- \@Id
172
- \@GeneratedValue(strategy = GenerationType.IDENTITY)
173
- private Long id;
174
-
175
- \@Column(name = "email", nullable = false, unique = true, length = 255)
176
- private String email;
177
-
178
- \@Column(name = "full_name", nullable = false, length = 100)
179
- private String fullName;
180
-
181
- \@Column(name = "deleted", nullable = false)
182
- \@Builder.Default
183
- private boolean deleted = false;
184
-
185
- \@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
186
- private List<Order> orders = new ArrayList<>();
187
- }
188
- ```
189
-
190
- ---
191
-
192
- ## DTO Rules
193
-
194
- - Use separate DTOs for **Request** and **Response** — never share
195
- - Use Lombok: `\@Getter`, `\@Builder`, `\@AllArgsConstructor`, `\@NoArgsConstructor`
196
- - Validate in the Request DTO with Bean Validation (`\@NotBlank`, `\@Email`, `\@NotNull`, `\@Size`)
197
- - Never expose internal fields (password hash, audit timestamps) in the Response DTO
198
-
199
- ```java
200
- // Request DTO
201
- \@Getter
202
- \@NoArgsConstructor
203
- \@AllArgsConstructor
204
- \@Builder
205
- public class CreateUserRequest {
206
-
207
- \@NotBlank(message = "Email is required")
208
- \@Email(message = "Invalid email format")
209
- private String email;
210
-
211
- \@NotBlank(message = "Full name is required")
212
- \@Size(min = 2, max = 100, message = "Full name must be 2-100 characters")
213
- private String fullName;
214
-
215
- \@NotBlank(message = "Password is required")
216
- \@Size(min = 8, message = "Password must be at least 8 characters")
217
- private String password;
218
- }
219
-
220
- // Response DTO
221
- \@Getter
222
- \@NoArgsConstructor
223
- \@AllArgsConstructor
224
- \@Builder
225
- public class UserResponse {
226
- private Long id;
227
- private String email;
228
- private String fullName;
229
- private LocalDateTime createdAt;
230
- }
231
- ```
232
-
233
- ---
234
-
235
- ## MapStruct Mapper Rules
236
-
237
- - Use `\@Mapper(componentModel = "spring")` — inject as a Spring bean
238
- - Define explicit mappings with `\@Mapping` when field names differ
239
- - Never do manual mapping (`new DTO(); dto.setField(entity.getField())`)
240
-
241
- ```java
242
- \@Mapper(componentModel = "spring")
243
- public interface UserMapper {
244
-
245
- \@Mapping(target = "createdAt", source = "createdAt")
246
- UserResponse toResponse(User user);
247
-
248
- \@Mapping(target = "id", ignore = true)
249
- \@Mapping(target = "deleted", ignore = true)
250
- \@Mapping(target = "createdAt", ignore = true)
251
- \@Mapping(target = "updatedAt", ignore = true)
252
- User toEntity(CreateUserRequest request);
253
-
254
- List<UserResponse> toResponseList(List<User> users);
255
- }
256
- ```
257
-
258
- ---
259
-
260
- ## Exception Handling
261
-
262
- - Create custom exceptions extending `RuntimeException`
263
- - Handle all exceptions in one `\@RestControllerAdvice` class
264
- - Return a consistent error response format
265
- - Never expose stack traces to the client
266
-
267
- ```java
268
- // Custom exception
269
- public class ResourceNotFoundException extends RuntimeException {
270
- public ResourceNotFoundException(String resource, Long id) {
271
- super(String.format("%s with id %d not found", resource, id));
272
- }
273
- }
274
-
275
- // Global handler
276
- \@RestControllerAdvice
277
- \@Slf4j
278
- public class GlobalExceptionHandler {
279
-
280
- \@ExceptionHandler(ResourceNotFoundException.class)
281
- public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
282
- log.warn("Resource not found: {}", ex.getMessage());
283
- return ResponseEntity.status(HttpStatus.NOT_FOUND)
284
- .body(ErrorResponse.of("NOT_FOUND", ex.getMessage()));
285
- }
286
-
287
- \@ExceptionHandler(MethodArgumentNotValidException.class)
288
- public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
289
- List<String> errors = ex.getBindingResult()
290
- .getFieldErrors()
291
- .stream()
292
- .map(e -> e.getField() + ": " + e.getDefaultMessage())
293
- .toList();
294
- return ResponseEntity.status(HttpStatus.BAD_REQUEST)
295
- .body(ErrorResponse.of("VALIDATION_FAILED", errors.toString()));
296
- }
297
-
298
- \@ExceptionHandler(Exception.class)
299
- public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
300
- log.error("Unexpected error", ex);
301
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
302
- .body(ErrorResponse.of("INTERNAL_ERROR", "An unexpected error occurred"));
303
- }
304
- }
305
- ```
306
-
307
- ---
308
-
309
- ## Testing Rules
310
-
311
- ### Unit Tests (Service layer)
312
- - Test class: `[ServiceImpl]Test.java`
313
- - Mock all dependencies with `\@ExtendWith(MockitoExtension.class)` + `\@Mock`
314
- - Test happy path + edge cases + exception scenarios
315
- - Use AssertJ: `assertThat(result).isEqualTo(expected)`
316
-
317
- ```java
318
- \@ExtendWith(MockitoExtension.class)
319
- class UserServiceImplTest {
320
-
321
- \@Mock
322
- private UserRepository userRepository;
323
-
324
- \@Mock
325
- private UserMapper userMapper;
326
-
327
- \@InjectMocks
328
- private UserServiceImpl userService;
329
-
330
- \@Test
331
- void create_shouldReturnUserResponse_whenEmailIsUnique() {
332
- // Arrange
333
- CreateUserRequest request = CreateUserRequest.builder()
334
- .email("test\@example.com")
335
- .fullName("Test User")
336
- .build();
337
- User user = User.builder().id(1L).email("test\@example.com").build();
338
- UserResponse expected = UserResponse.builder().id(1L).build();
339
-
340
- given(userRepository.existsByEmail(request.getEmail())).willReturn(false);
341
- given(userMapper.toEntity(request)).willReturn(user);
342
- given(userRepository.save(user)).willReturn(user);
343
- given(userMapper.toResponse(user)).willReturn(expected);
344
-
345
- // Act
346
- UserResponse result = userService.create(request);
347
-
348
- // Assert
349
- assertThat(result).isEqualTo(expected);
350
- verify(userRepository).save(user);
351
- }
352
-
353
- \@Test
354
- void create_shouldThrowDuplicateEmailException_whenEmailAlreadyExists() {
355
- CreateUserRequest request = CreateUserRequest.builder()
356
- .email("existing\@example.com").build();
357
- given(userRepository.existsByEmail(request.getEmail())).willReturn(true);
358
-
359
- assertThatThrownBy(() -> userService.create(request))
360
- .isInstanceOf(DuplicateEmailException.class);
361
- verify(userRepository, never()).save(any());
362
- }
363
- }
364
- ```
365
-
366
- ### Integration Tests (Controller layer)
367
- - Use `\@SpringBootTest` + `\@AutoConfigureMockMvc`
368
- - Test full HTTP flow with `MockMvc`
369
- - Use `\@Sql` or Testcontainers for database state
370
-
371
- ```java
372
- \@SpringBootTest
373
- \@AutoConfigureMockMvc
374
- class UserControllerIntegrationTest {
375
-
376
- \@Autowired
377
- private MockMvc mockMvc;
378
-
379
- \@Autowired
380
- private ObjectMapper objectMapper;
381
-
382
- \@Test
383
- void createUser_shouldReturn201_whenRequestIsValid() throws Exception {
384
- CreateUserRequest request = CreateUserRequest.builder()
385
- .email("test\@example.com")
386
- .fullName("Test User")
387
- .password("password123")
388
- .build();
389
-
390
- mockMvc.perform(post("/api/v1/users")
391
- .contentType(MediaType.APPLICATION_JSON)
392
- .content(objectMapper.writeValueAsString(request)))
393
- .andExpect(status().isCreated())
394
- .andExpect(jsonPath("$.email").value("test\@example.com"))
395
- .andExpect(jsonPath("$.id").isNumber());
396
- }
397
-
398
- \@Test
399
- void createUser_shouldReturn400_whenEmailIsInvalid() throws Exception {
400
- CreateUserRequest request = CreateUserRequest.builder()
401
- .email("not-an-email")
402
- .fullName("Test")
403
- .password("password123")
404
- .build();
405
-
406
- mockMvc.perform(post("/api/v1/users")
407
- .contentType(MediaType.APPLICATION_JSON)
408
- .content(objectMapper.writeValueAsString(request)))
409
- .andExpect(status().isBadRequest())
410
- .andExpect(jsonPath("$.code").value("VALIDATION_FAILED"));
411
- }
412
- }
413
- ```
414
-
415
- ---
416
-
417
- ## Naming Conventions
418
-
419
- | Element | Convention | Example |
420
- |---------|-----------|---------|
421
- | Class | PascalCase | `UserService`, `OrderController` |
422
- | Method | camelCase | `findById`, `createOrder` |
423
- | Variable | camelCase | `userResponse`, `orderId` |
424
- | Constant | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
425
- | Package | lowercase | `com.company.project.service` |
426
- | DB Table | snake_case | `user_orders`, `product_items` |
427
- | DB Column | snake_case | `created_at`, `user_id` |
428
- | REST endpoint | kebab-case | `/api/v1/user-profiles` |
429
- | Request DTO | `[Action][Resource]Request` | `CreateOrderRequest` |
430
- | Response DTO | `[Resource]Response` | `OrderResponse` |
431
-
432
- ---
433
-
434
- ## API Design
435
-
436
- - Version all APIs: `/api/v1/...`
437
- - Use plural nouns for resources: `/users`, `/orders`
438
- - HTTP methods: `GET` (read), `POST` (create), `PUT` (full update), `PATCH` (partial), `DELETE`
439
- - Return `201 Created` for POST, `200 OK` for GET/PUT/PATCH, `204 No Content` for DELETE
440
- - Use consistent pagination: `?page=0&size=20&sort=createdAt,desc`
441
-
442
- ```
443
- GET /api/v1/users → 200 list
444
- POST /api/v1/users → 201 created
445
- GET /api/v1/users/{id} → 200 or 404
446
- PUT /api/v1/users/{id} → 200 or 404
447
- DELETE /api/v1/users/{id} → 204 or 404
448
- GET /api/v1/users/{id}/orders → 200 nested resource
449
- ```
450
-
451
- ---
452
-
453
- ## Performance Rules
454
-
455
- - Always use pagination — never return unbounded lists
456
- - Avoid N+1: use `\@EntityGraph` or `JOIN FETCH` in JPQL
457
- - Add database indexes on frequently queried columns
458
- - Use `\@Transactional(readOnly = true)` on read-only service methods
459
- - For heavy read operations, consider projection interfaces
460
-
461
- ```java
462
- // ✅ Read-only transaction
463
- \@Transactional(readOnly = true)
464
- public Page<UserResponse> findAll(Pageable pageable) {
465
- return userRepository.findAll(pageable).map(userMapper::toResponse);
466
- }
467
- ```
468
-
469
- ---
470
-
471
- ## Security Rules
472
-
473
- - Never log passwords, tokens, or sensitive PII
474
- - Hash passwords with BCrypt: `passwordEncoder.encode(rawPassword)`
475
- - Validate and sanitize all user inputs via Bean Validation
476
- - Use `\@PreAuthorize` for method-level security
477
- - Never return stack traces to API consumers
478
- - Store secrets in environment variables / Vault — never in code
479
-
480
- ---
481
-
482
- ## Logging Rules
483
-
484
- - Use SLF4J with Lombok `\@Slf4j`
485
- - `log.info` — normal business events
486
- - `log.warn` — recoverable issues (not found, validation fail)
487
- - `log.error` — unexpected exceptions (always include `ex` as the second argument)
488
- - Never log sensitive data (password, credit card, token)
489
-
490
- ```java
491
- \@Slf4j
492
- \@Service
493
- public class UserServiceImpl implements UserService {
494
-
495
- public UserResponse findById(Long id) {
496
- log.info("Finding user by id: {}", id);
497
- return userRepository.findById(id)
498
- .map(userMapper::toResponse)
499
- .orElseThrow(() -> {
500
- log.warn("User not found: id={}", id);
501
- return new ResourceNotFoundException("User", id);
502
- });
503
- }
504
- }
505
- ```
506
-
507
- ---
508
-
509
- ## Common Anti-Patterns to Avoid
510
-
511
- - ❌ `\@Autowired` field injection → use constructor injection (Lombok `\@RequiredArgsConstructor`)
512
- - ❌ `\@Data` on JPA entities → use `\@Getter \@Setter` separately
513
- - ❌ Returning `Entity` directly from Controller → always use DTO
514
- - ❌ `SELECT *` or unbounded `findAll()` → always paginate
515
- - ❌ Business logic in Controller → move to Service
516
- - ❌ Catching and swallowing exceptions → handle properly or rethrow
517
- - ❌ `new RuntimeException("something")` → create a specific custom exception
518
- - ❌ Hardcoding config values → use `\@Value` or `\@ConfigurationProperties`
519
- - ❌ `\@Transactional` on Controller → only on Service methods
520
-
521
- ---
522
-
523
- 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.
1
+ # Spring Boot AI System Prompt
2
+
3
+ You are an expert Java Spring Boot developer. Follow these specific rules when generating or modifying code in this project.
4
+
5
+ > **Code examples:** See `.rules/java/spring-boot-examples.md` — read it when generating code for a specific layer.
6
+
7
+ ---
8
+
9
+ ## Project Stack
10
+
11
+ - **Java:** 17+
12
+ - **Spring Boot:** 3.x
13
+ - **Build Tool:** Maven (prefer) or Gradle
14
+ - **ORM:** Spring Data JPA + Hibernate
15
+ - **Database:** MySQL / PostgreSQL
16
+ - **Testing:** JUnit 5 + Mockito + AssertJ
17
+ - **Utilities:** Lombok, MapStruct
18
+ - **API Style:** RESTful JSON
19
+
20
+ ---
21
+
22
+ ## Architecture
23
+
24
+ Follow strict **Layered Architecture**:
25
+
26
+ ```
27
+ Controller (HTTP layer)
28
+
29
+ Service (Business logic)
30
+
31
+ Repository (Data access - Spring Data JPA)
32
+
33
+ Entity / Domain Model
34
+ ```
35
+
36
+ ### Package Structure
37
+
38
+ ```
39
+ com.company.project/
40
+ ├── controller/ # \@RestControllerHTTP endpoints only
41
+ ├── service/
42
+ ├── impl/ # \@Servicebusiness logic implementation
43
+ │ └── [Interface].java
44
+ ├── repository/ # \@Repository — extends JpaRepository
45
+ ├── entity/ # \@Entity JPA entities
46
+ ├── dto/
47
+ ├── request/ # Input DTOs (e.g. CreateUserRequest)
48
+ │ └── response/ # Output DTOs (e.g. UserResponse)
49
+ ├── mapper/ # MapStruct mappers (Entity ↔ DTO)
50
+ ├── exception/
51
+ ├── GlobalExceptionHandler.java # \@RestControllerAdvice
52
+ └── [CustomException].java
53
+ ├── config/ # \@Configuration classes
54
+ └── util/ # Pure utility helpers (stateless)
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Controller Rules
60
+
61
+ - Annotate with `\@RestController` + `\@RequestMapping`
62
+ - **Only** handle HTTP concerns: parse request, call service, return response
63
+ - Never put business logic in Controller
64
+ - Always use DTOs never expose Entity directly
65
+ - Use `ResponseEntity<T>` for explicit HTTP status control
66
+ - Validate input with `\@Valid` + Bean Validation annotations
67
+
68
+ ---
69
+
70
+ ## Service Rules
71
+
72
+ - Always define an **interface**, implement in the `impl/` package
73
+ - Annotate implementation with `\@Service`
74
+ - All business logic lives here
75
+ - Use `\@Transactional` at the method level (not class level)
76
+ - Throw specific custom exceptions, not generic `RuntimeException`
77
+ - Never return an Entity — always convert to DTO via Mapper
78
+
79
+ ---
80
+
81
+ ## Repository Rules
82
+
83
+ - Extend `JpaRepository<Entity, ID>`
84
+ - Use **method name queries** for simple queries
85
+ - Use `\@Query` (JPQL) for complex queries — avoid native SQL unless necessary
86
+ - Never add business logic here
87
+ - Use `\@EntityGraph` to solve N+1 problems
88
+
89
+ ---
90
+
91
+ ## Entity Rules
92
+
93
+ - Use Lombok: `\@Getter`, `\@Setter`, `\@NoArgsConstructor`, `\@AllArgsConstructor`, `\@Builder`
94
+ - Avoid `\@Data` on entities (causes issues with `equals/hashCode` + lazy loading)
95
+ - Always use `\@Table(name = "snake_case_table_name")`
96
+ - Use `\@Column(name = "snake_case_column_name")` explicitly
97
+ - For soft delete: add `deleted` boolean + `deletedAt` timestamp
98
+ - Extend `BaseEntity` for audit fields (`createdAt`, `updatedAt`)
99
+
100
+ ---
101
+
102
+ ## DTO Rules
103
+
104
+ - Use separate DTOs for **Request** and **Response** — never share
105
+ - Use Lombok: `\@Getter`, `\@Builder`, `\@AllArgsConstructor`, `\@NoArgsConstructor`
106
+ - Validate in the Request DTO with Bean Validation (`\@NotBlank`, `\@Email`, `\@NotNull`, `\@Size`)
107
+ - Never expose internal fields (password hash, audit timestamps) in the Response DTO
108
+
109
+ ---
110
+
111
+ ## MapStruct Mapper Rules
112
+
113
+ - Use `\@Mapper(componentModel = "spring")` — inject as a Spring bean
114
+ - Define explicit mappings with `\@Mapping` when field names differ
115
+ - Never do manual mapping (`new DTO(); dto.setField(entity.getField())`)
116
+
117
+ ---
118
+
119
+ ## Exception Handling
120
+
121
+ - Create custom exceptions extending `RuntimeException`
122
+ - Handle all exceptions in one `\@RestControllerAdvice` class
123
+ - Return a consistent error response format
124
+ - Never expose stack traces to the client
125
+
126
+ ---
127
+
128
+ ## Testing Rules
129
+
130
+ ### Unit Tests (Service layer)
131
+ - Test class: `[ServiceImpl]Test.java`
132
+ - Mock all dependencies with `\@ExtendWith(MockitoExtension.class)` + `\@Mock`
133
+ - Test happy path + edge cases + exception scenarios
134
+ - Use AssertJ: `assertThat(result).isEqualTo(expected)`
135
+
136
+ ### Integration Tests (Controller layer)
137
+ - Use `\@SpringBootTest` + `\@AutoConfigureMockMvc`
138
+ - Test full HTTP flow with `MockMvc`
139
+ - Use `\@Sql` or Testcontainers for database state
140
+
141
+ ---
142
+
143
+ ## Naming Conventions
144
+
145
+ | Element | Convention | Example |
146
+ |---------|-----------|---------|
147
+ | Class | PascalCase | `UserService`, `OrderController` |
148
+ | Method | camelCase | `findById`, `createOrder` |
149
+ | Variable | camelCase | `userResponse`, `orderId` |
150
+ | Constant | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
151
+ | Package | lowercase | `com.company.project.service` |
152
+ | DB Table | snake_case | `user_orders`, `product_items` |
153
+ | DB Column | snake_case | `created_at`, `user_id` |
154
+ | REST endpoint | kebab-case | `/api/v1/user-profiles` |
155
+ | Request DTO | `[Action][Resource]Request` | `CreateOrderRequest` |
156
+ | Response DTO | `[Resource]Response` | `OrderResponse` |
157
+
158
+ ---
159
+
160
+ ## API Design
161
+
162
+ - Version all APIs: `/api/v1/...`
163
+ - Use plural nouns for resources: `/users`, `/orders`
164
+ - HTTP methods: `GET` (read), `POST` (create), `PUT` (full update), `PATCH` (partial), `DELETE`
165
+ - Return `201 Created` for POST, `200 OK` for GET/PUT/PATCH, `204 No Content` for DELETE
166
+ - Use consistent pagination: `?page=0&size=20&sort=createdAt,desc`
167
+
168
+ ```
169
+ GET /api/v1/users → 200 list
170
+ POST /api/v1/users → 201 created
171
+ GET /api/v1/users/{id} → 200 or 404
172
+ PUT /api/v1/users/{id} → 200 or 404
173
+ DELETE /api/v1/users/{id} → 204 or 404
174
+ GET /api/v1/users/{id}/orders → 200 nested resource
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Performance Rules
180
+
181
+ - Always use pagination never return unbounded lists
182
+ - Avoid N+1: use `\@EntityGraph` or `JOIN FETCH` in JPQL
183
+ - Add database indexes on frequently queried columns
184
+ - Use `\@Transactional(readOnly = true)` on read-only service methods
185
+ - For heavy read operations, consider projection interfaces
186
+
187
+ ---
188
+
189
+ ## Security Rules
190
+
191
+ - Never log passwords, tokens, or sensitive PII
192
+ - Hash passwords with BCrypt: `passwordEncoder.encode(rawPassword)`
193
+ - Validate and sanitize all user inputs via Bean Validation
194
+ - Use `\@PreAuthorize` for method-level security
195
+ - Never return stack traces to API consumers
196
+ - Store secrets in environment variables / Vault never in code
197
+
198
+ ---
199
+
200
+ ## Logging Rules
201
+
202
+ - Use SLF4J with Lombok `\@Slf4j`
203
+ - `log.info` — normal business events
204
+ - `log.warn` — recoverable issues (not found, validation fail)
205
+ - `log.error` unexpected exceptions (always include `ex` as the second argument)
206
+ - Never log sensitive data (password, credit card, token)
207
+
208
+ ---
209
+
210
+ ## Common Anti-Patterns to Avoid
211
+
212
+ - `\@Autowired` field injection use constructor injection (Lombok `\@RequiredArgsConstructor`)
213
+ - `\@Data` on JPA entities → use `\@Getter \@Setter` separately
214
+ - ❌ Returning `Entity` directly from Controller → always use DTO
215
+ - `SELECT *` or unbounded `findAll()` → always paginate
216
+ - Business logic in Controller move to Service
217
+ - Catching and swallowing exceptions → handle properly or rethrow
218
+ - ❌ `new RuntimeException("something")` → create a specific custom exception
219
+ - ❌ Hardcoding config values → use `\@Value` or `\@ConfigurationProperties`
220
+ - `\@Transactional` on Controller → only on Service methods
221
+
222
+ ---
223
+
224
+ 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.