omgkit 2.1.1 → 2.2.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.
Files changed (50) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/SKILL_STANDARDS.md +743 -0
  3. package/plugin/skills/databases/mongodb/SKILL.md +797 -28
  4. package/plugin/skills/databases/prisma/SKILL.md +776 -30
  5. package/plugin/skills/databases/redis/SKILL.md +885 -25
  6. package/plugin/skills/devops/aws/SKILL.md +686 -28
  7. package/plugin/skills/devops/github-actions/SKILL.md +684 -29
  8. package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
  9. package/plugin/skills/frameworks/django/SKILL.md +920 -20
  10. package/plugin/skills/frameworks/express/SKILL.md +1361 -35
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
  12. package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
  14. package/plugin/skills/frameworks/rails/SKILL.md +594 -28
  15. package/plugin/skills/frameworks/spring/SKILL.md +528 -35
  16. package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
  17. package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
  18. package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
  19. package/plugin/skills/frontend/responsive/SKILL.md +847 -21
  20. package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
  21. package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
  22. package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
  23. package/plugin/skills/languages/javascript/SKILL.md +935 -31
  24. package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
  25. package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
  26. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
  27. package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
  28. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
  29. package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
  30. package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
  31. package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
  32. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
  33. package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
  34. package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
  35. package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
  36. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
  37. package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
  38. package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
  39. package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
  40. package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
  41. package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
  42. package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
  43. package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
  44. package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
  45. package/plugin/skills/security/better-auth/SKILL.md +1065 -28
  46. package/plugin/skills/security/oauth/SKILL.md +968 -31
  47. package/plugin/skills/security/owasp/SKILL.md +894 -33
  48. package/plugin/skills/testing/playwright/SKILL.md +764 -38
  49. package/plugin/skills/testing/pytest/SKILL.md +873 -36
  50. package/plugin/skills/testing/vitest/SKILL.md +980 -35
@@ -1,70 +1,563 @@
1
1
  ---
2
2
  name: spring
3
- description: Spring Boot development. Use for Spring projects, REST APIs, JPA.
3
+ description: Enterprise Spring Boot development with JPA, security, testing, and microservices patterns
4
+ category: frameworks
5
+ triggers:
6
+ - spring
7
+ - spring boot
8
+ - java spring
9
+ - spring mvc
10
+ - spring security
11
+ - jpa
12
+ - hibernate
13
+ - java api
4
14
  ---
5
15
 
6
- # Spring Boot Skill
16
+ # Spring Boot
7
17
 
8
- ## Patterns
18
+ Enterprise-grade **Spring Boot development** following industry best practices. This skill covers Spring Data JPA, Spring Security, REST APIs, validation, testing patterns, and microservices configurations used by top engineering teams.
19
+
20
+ ## Purpose
21
+
22
+ Build scalable Java applications with confidence:
23
+
24
+ - Design clean architectures with Spring Boot
25
+ - Implement REST APIs with proper validation
26
+ - Use Spring Data JPA for database operations
27
+ - Handle authentication with Spring Security
28
+ - Write comprehensive tests with JUnit and MockMvc
29
+ - Deploy production-ready applications
30
+ - Build microservices with Spring Cloud
31
+
32
+ ## Features
33
+
34
+ ### 1. Entity Design and Relationships
35
+
36
+ ```java
37
+ // src/main/java/com/example/model/User.java
38
+ package com.example.model;
39
+
40
+ import jakarta.persistence.*;
41
+ import lombok.*;
42
+ import org.hibernate.annotations.CreationTimestamp;
43
+ import org.hibernate.annotations.UpdateTimestamp;
44
+ import org.hibernate.annotations.UuidGenerator;
45
+
46
+ import java.time.LocalDateTime;
47
+ import java.util.HashSet;
48
+ import java.util.Set;
49
+ import java.util.UUID;
50
+
51
+ @Entity
52
+ @Table(name = "users")
53
+ @Getter @Setter
54
+ @NoArgsConstructor @AllArgsConstructor
55
+ @Builder
56
+ public class User {
57
+ @Id
58
+ @UuidGenerator
59
+ private UUID id;
60
+
61
+ @Column(nullable = false, unique = true)
62
+ private String email;
63
+
64
+ @Column(nullable = false)
65
+ private String name;
66
+
67
+ @Column(nullable = false)
68
+ private String password;
69
+
70
+ @Enumerated(EnumType.STRING)
71
+ @Builder.Default
72
+ private UserRole role = UserRole.USER;
73
+
74
+ @Builder.Default
75
+ private Boolean isActive = true;
76
+
77
+ @CreationTimestamp
78
+ private LocalDateTime createdAt;
79
+
80
+ @UpdateTimestamp
81
+ private LocalDateTime updatedAt;
82
+
83
+ @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
84
+ @Builder.Default
85
+ private Set<Membership> memberships = new HashSet<>();
86
+
87
+ public boolean isAdmin() {
88
+ return this.role == UserRole.ADMIN;
89
+ }
90
+ }
91
+
92
+
93
+ // src/main/java/com/example/model/Organization.java
94
+ @Entity
95
+ @Table(name = "organizations")
96
+ @Getter @Setter
97
+ @NoArgsConstructor @AllArgsConstructor
98
+ @Builder
99
+ public class Organization {
100
+ @Id
101
+ @UuidGenerator
102
+ private UUID id;
103
+
104
+ @Column(nullable = false)
105
+ private String name;
106
+
107
+ @Column(nullable = false, unique = true)
108
+ private String slug;
109
+
110
+ @ManyToOne(fetch = FetchType.LAZY)
111
+ @JoinColumn(name = "owner_id", nullable = false)
112
+ private User owner;
113
+
114
+ @OneToMany(mappedBy = "organization", cascade = CascadeType.ALL)
115
+ @Builder.Default
116
+ private Set<Membership> memberships = new HashSet<>();
117
+
118
+ @CreationTimestamp
119
+ private LocalDateTime createdAt;
120
+ }
121
+ ```
122
+
123
+ ### 2. DTOs and Validation
124
+
125
+ ```java
126
+ // src/main/java/com/example/dto/user/CreateUserRequest.java
127
+ package com.example.dto.user;
128
+
129
+ import jakarta.validation.constraints.*;
130
+ import lombok.Data;
131
+
132
+ @Data
133
+ public class CreateUserRequest {
134
+ @NotBlank(message = "Name is required")
135
+ @Size(min = 2, max = 100)
136
+ private String name;
137
+
138
+ @NotBlank(message = "Email is required")
139
+ @Email(message = "Invalid email format")
140
+ private String email;
141
+
142
+ @NotBlank(message = "Password is required")
143
+ @Size(min = 8, max = 128)
144
+ @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&]).*$",
145
+ message = "Password must contain uppercase, lowercase, number and special character")
146
+ private String password;
147
+
148
+ private String role;
149
+ }
150
+
151
+
152
+ // src/main/java/com/example/dto/user/UserResponse.java
153
+ @Data
154
+ @Builder
155
+ public class UserResponse {
156
+ private UUID id;
157
+ private String email;
158
+ private String name;
159
+ private UserRole role;
160
+ private Boolean isActive;
161
+ private LocalDateTime createdAt;
162
+
163
+ public static UserResponse fromEntity(User user) {
164
+ return UserResponse.builder()
165
+ .id(user.getId())
166
+ .email(user.getEmail())
167
+ .name(user.getName())
168
+ .role(user.getRole())
169
+ .isActive(user.getIsActive())
170
+ .createdAt(user.getCreatedAt())
171
+ .build();
172
+ }
173
+ }
174
+
175
+
176
+ // src/main/java/com/example/dto/common/PaginatedResponse.java
177
+ @Data
178
+ @Builder
179
+ public class PaginatedResponse<T> {
180
+ private List<T> data;
181
+ private int page;
182
+ private int limit;
183
+ private long total;
184
+ private int totalPages;
185
+ private boolean hasMore;
186
+
187
+ public static <T, E> PaginatedResponse<T> fromPage(
188
+ Page<E> page,
189
+ java.util.function.Function<E, T> mapper
190
+ ) {
191
+ return PaginatedResponse.<T>builder()
192
+ .data(page.getContent().stream().map(mapper).toList())
193
+ .page(page.getNumber() + 1)
194
+ .limit(page.getSize())
195
+ .total(page.getTotalElements())
196
+ .totalPages(page.getTotalPages())
197
+ .hasMore(page.hasNext())
198
+ .build();
199
+ }
200
+ }
201
+ ```
202
+
203
+ ### 3. Repositories
204
+
205
+ ```java
206
+ // src/main/java/com/example/repository/UserRepository.java
207
+ @Repository
208
+ public interface UserRepository extends JpaRepository<User, UUID> {
209
+ Optional<User> findByEmail(String email);
210
+
211
+ boolean existsByEmail(String email);
212
+
213
+ @Query("""
214
+ SELECT u FROM User u
215
+ WHERE u.isActive = true
216
+ AND (:search IS NULL OR LOWER(u.name) LIKE LOWER(CONCAT('%', :search, '%'))
217
+ OR LOWER(u.email) LIKE LOWER(CONCAT('%', :search, '%')))
218
+ AND (:role IS NULL OR u.role = :role)
219
+ """)
220
+ Page<User> findAllWithFilters(
221
+ @Param("search") String search,
222
+ @Param("role") UserRole role,
223
+ Pageable pageable
224
+ );
225
+ }
226
+ ```
227
+
228
+ ### 4. Services
229
+
230
+ ```java
231
+ // src/main/java/com/example/service/UserService.java
232
+ @Service
233
+ @RequiredArgsConstructor
234
+ @Transactional(readOnly = true)
235
+ public class UserService {
236
+ private final UserRepository userRepository;
237
+ private final PasswordEncoder passwordEncoder;
238
+
239
+ public PaginatedResponse<UserResponse> findAll(String search, String role, int page, int limit) {
240
+ UserRole userRole = role != null ? UserRole.valueOf(role.toUpperCase()) : null;
241
+ PageRequest pageRequest = PageRequest.of(page - 1, limit, Sort.by("createdAt").descending());
242
+
243
+ Page<User> users = userRepository.findAllWithFilters(search, userRole, pageRequest);
244
+
245
+ return PaginatedResponse.fromPage(users, UserResponse::fromEntity);
246
+ }
247
+
248
+ public UserResponse findById(UUID id) {
249
+ User user = userRepository.findById(id)
250
+ .orElseThrow(() -> new NotFoundException("User not found with id: " + id));
251
+ return UserResponse.fromEntity(user);
252
+ }
253
+
254
+ @Transactional
255
+ public UserResponse create(CreateUserRequest request) {
256
+ if (userRepository.existsByEmail(request.getEmail())) {
257
+ throw new ConflictException("Email already in use");
258
+ }
259
+
260
+ User user = User.builder()
261
+ .email(request.getEmail().toLowerCase())
262
+ .name(request.getName())
263
+ .password(passwordEncoder.encode(request.getPassword()))
264
+ .role(request.getRole() != null ? UserRole.valueOf(request.getRole().toUpperCase()) : UserRole.USER)
265
+ .build();
266
+
267
+ return UserResponse.fromEntity(userRepository.save(user));
268
+ }
269
+
270
+ @Transactional
271
+ public UserResponse update(UUID id, UpdateUserRequest request) {
272
+ User user = userRepository.findById(id)
273
+ .orElseThrow(() -> new NotFoundException("User not found"));
274
+
275
+ if (request.getEmail() != null && !request.getEmail().equals(user.getEmail())) {
276
+ if (userRepository.existsByEmail(request.getEmail())) {
277
+ throw new ConflictException("Email already in use");
278
+ }
279
+ user.setEmail(request.getEmail().toLowerCase());
280
+ }
281
+
282
+ if (request.getName() != null) user.setName(request.getName());
283
+ if (request.getIsActive() != null) user.setIsActive(request.getIsActive());
284
+
285
+ return UserResponse.fromEntity(userRepository.save(user));
286
+ }
287
+
288
+ @Transactional
289
+ public void delete(UUID id) {
290
+ if (!userRepository.existsById(id)) {
291
+ throw new NotFoundException("User not found");
292
+ }
293
+ userRepository.deleteById(id);
294
+ }
295
+ }
296
+ ```
297
+
298
+ ### 5. Controllers
9
299
 
10
- ### Controller
11
300
  ```java
301
+ // src/main/java/com/example/controller/UserController.java
12
302
  @RestController
13
- @RequestMapping("/api/users")
303
+ @RequestMapping("/api/v1/users")
304
+ @RequiredArgsConstructor
305
+ @Tag(name = "Users", description = "User management endpoints")
14
306
  public class UserController {
15
- @Autowired
16
- private UserService userService;
307
+ private final UserService userService;
17
308
 
18
309
  @GetMapping
19
- public List<User> findAll() {
20
- return userService.findAll();
310
+ @PreAuthorize("hasRole('ADMIN')")
311
+ @Operation(summary = "List all users")
312
+ public ResponseEntity<PaginatedResponse<UserResponse>> findAll(
313
+ @RequestParam(required = false) String search,
314
+ @RequestParam(required = false) String role,
315
+ @RequestParam(defaultValue = "1") int page,
316
+ @RequestParam(defaultValue = "20") int limit
317
+ ) {
318
+ return ResponseEntity.ok(userService.findAll(search, role, page, limit));
319
+ }
320
+
321
+ @GetMapping("/me")
322
+ @Operation(summary = "Get current user profile")
323
+ public ResponseEntity<UserResponse> getCurrentUser(@AuthenticationPrincipal User currentUser) {
324
+ return ResponseEntity.ok(userService.findById(currentUser.getId()));
325
+ }
326
+
327
+ @GetMapping("/{id}")
328
+ @PreAuthorize("hasRole('ADMIN')")
329
+ public ResponseEntity<UserResponse> findById(@PathVariable UUID id) {
330
+ return ResponseEntity.ok(userService.findById(id));
21
331
  }
22
332
 
23
333
  @PostMapping
24
- public ResponseEntity<User> create(@RequestBody CreateUserDto dto) {
25
- User user = userService.create(dto);
26
- return ResponseEntity.status(HttpStatus.CREATED).body(user);
334
+ @PreAuthorize("hasRole('ADMIN')")
335
+ public ResponseEntity<UserResponse> create(@Valid @RequestBody CreateUserRequest request) {
336
+ return ResponseEntity.status(HttpStatus.CREATED).body(userService.create(request));
337
+ }
338
+
339
+ @PatchMapping("/{id}")
340
+ @PreAuthorize("hasRole('ADMIN')")
341
+ public ResponseEntity<UserResponse> update(@PathVariable UUID id, @Valid @RequestBody UpdateUserRequest request) {
342
+ return ResponseEntity.ok(userService.update(id, request));
343
+ }
344
+
345
+ @DeleteMapping("/{id}")
346
+ @PreAuthorize("hasRole('ADMIN')")
347
+ @ResponseStatus(HttpStatus.NO_CONTENT)
348
+ public void delete(@PathVariable UUID id) {
349
+ userService.delete(id);
27
350
  }
28
351
  }
29
352
  ```
30
353
 
31
- ### Service
354
+ ### 6. Security Configuration
355
+
32
356
  ```java
33
- @Service
34
- public class UserService {
35
- @Autowired
36
- private UserRepository userRepository;
357
+ // src/main/java/com/example/config/SecurityConfig.java
358
+ @Configuration
359
+ @EnableWebSecurity
360
+ @EnableMethodSecurity
361
+ @RequiredArgsConstructor
362
+ public class SecurityConfig {
363
+ private final JwtAuthenticationFilter jwtAuthFilter;
364
+
365
+ @Bean
366
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
367
+ http
368
+ .csrf(csrf -> csrf.disable())
369
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
370
+ .authorizeHttpRequests(auth -> auth
371
+ .requestMatchers("/api/v1/auth/**").permitAll()
372
+ .requestMatchers("/actuator/health").permitAll()
373
+ .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
374
+ .anyRequest().authenticated()
375
+ )
376
+ .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
377
+
378
+ return http.build();
379
+ }
37
380
 
38
- public List<User> findAll() {
39
- return userRepository.findAll();
381
+ @Bean
382
+ public PasswordEncoder passwordEncoder() {
383
+ return new BCryptPasswordEncoder();
40
384
  }
41
385
  }
42
386
  ```
43
387
 
44
- ### Repository
388
+ ### 7. Testing
389
+
45
390
  ```java
46
- @Repository
47
- public interface UserRepository extends JpaRepository<User, Long> {
48
- Optional<User> findByEmail(String email);
391
+ // src/test/java/com/example/service/UserServiceTest.java
392
+ @ExtendWith(MockitoExtension.class)
393
+ class UserServiceTest {
394
+ @Mock
395
+ private UserRepository userRepository;
396
+
397
+ @Mock
398
+ private PasswordEncoder passwordEncoder;
399
+
400
+ @InjectMocks
401
+ private UserService userService;
402
+
403
+ private User testUser;
404
+
405
+ @BeforeEach
406
+ void setUp() {
407
+ testUser = User.builder()
408
+ .id(UUID.randomUUID())
409
+ .email("test@example.com")
410
+ .name("Test User")
411
+ .role(UserRole.USER)
412
+ .isActive(true)
413
+ .build();
414
+ }
415
+
416
+ @Test
417
+ void findById_ShouldReturnUser_WhenUserExists() {
418
+ when(userRepository.findById(testUser.getId())).thenReturn(Optional.of(testUser));
419
+
420
+ UserResponse result = userService.findById(testUser.getId());
421
+
422
+ assertThat(result.getEmail()).isEqualTo(testUser.getEmail());
423
+ }
424
+
425
+ @Test
426
+ void findById_ShouldThrowNotFoundException_WhenUserNotFound() {
427
+ UUID id = UUID.randomUUID();
428
+ when(userRepository.findById(id)).thenReturn(Optional.empty());
429
+
430
+ assertThatThrownBy(() -> userService.findById(id))
431
+ .isInstanceOf(NotFoundException.class);
432
+ }
433
+
434
+ @Test
435
+ void create_ShouldCreateUser_WhenEmailIsUnique() {
436
+ CreateUserRequest request = new CreateUserRequest();
437
+ request.setEmail("new@example.com");
438
+ request.setName("New User");
439
+ request.setPassword("Password123!");
440
+
441
+ when(userRepository.existsByEmail(request.getEmail())).thenReturn(false);
442
+ when(passwordEncoder.encode(request.getPassword())).thenReturn("encoded");
443
+ when(userRepository.save(any(User.class))).thenReturn(testUser);
444
+
445
+ UserResponse result = userService.create(request);
446
+
447
+ assertThat(result).isNotNull();
448
+ verify(userRepository).save(any(User.class));
449
+ }
450
+
451
+ @Test
452
+ void create_ShouldThrowConflictException_WhenEmailExists() {
453
+ CreateUserRequest request = new CreateUserRequest();
454
+ request.setEmail("existing@example.com");
455
+
456
+ when(userRepository.existsByEmail(request.getEmail())).thenReturn(true);
457
+
458
+ assertThatThrownBy(() -> userService.create(request))
459
+ .isInstanceOf(ConflictException.class);
460
+ }
461
+ }
462
+
463
+
464
+ // src/test/java/com/example/controller/UserControllerTest.java
465
+ @WebMvcTest(UserController.class)
466
+ class UserControllerTest {
467
+ @Autowired
468
+ private MockMvc mockMvc;
469
+
470
+ @Autowired
471
+ private ObjectMapper objectMapper;
472
+
473
+ @MockBean
474
+ private UserService userService;
475
+
476
+ @Test
477
+ @WithMockUser(roles = "ADMIN")
478
+ void create_ShouldReturn201_WhenValidRequest() throws Exception {
479
+ CreateUserRequest request = new CreateUserRequest();
480
+ request.setEmail("test@example.com");
481
+ request.setName("Test User");
482
+ request.setPassword("Password123!");
483
+
484
+ UserResponse response = UserResponse.builder()
485
+ .email("test@example.com")
486
+ .name("Test User")
487
+ .build();
488
+
489
+ when(userService.create(any())).thenReturn(response);
490
+
491
+ mockMvc.perform(post("/api/v1/users")
492
+ .contentType(MediaType.APPLICATION_JSON)
493
+ .content(objectMapper.writeValueAsString(request)))
494
+ .andExpect(status().isCreated())
495
+ .andExpect(jsonPath("$.email").value("test@example.com"));
496
+ }
497
+
498
+ @Test
499
+ @WithMockUser(roles = "USER")
500
+ void findAll_ShouldReturn403_WhenNotAdmin() throws Exception {
501
+ mockMvc.perform(get("/api/v1/users"))
502
+ .andExpect(status().isForbidden());
503
+ }
49
504
  }
50
505
  ```
51
506
 
52
- ### Entity
507
+ ## Use Cases
508
+
509
+ ### Caching with Redis
510
+
53
511
  ```java
54
- @Entity
55
- @Table(name = "users")
56
- public class User {
57
- @Id
58
- @GeneratedValue(strategy = GenerationType.IDENTITY)
59
- private Long id;
512
+ @Service
513
+ @RequiredArgsConstructor
514
+ public class CachedUserService {
515
+ private final UserRepository userRepository;
60
516
 
61
- @Column(unique = true)
62
- private String email;
517
+ @Cacheable(value = "users", key = "#id")
518
+ public UserResponse findById(UUID id) {
519
+ User user = userRepository.findById(id)
520
+ .orElseThrow(() -> new NotFoundException("User not found"));
521
+ return UserResponse.fromEntity(user);
522
+ }
523
+
524
+ @CacheEvict(value = "users", key = "#id")
525
+ public void evictCache(UUID id) {}
63
526
  }
64
527
  ```
65
528
 
66
529
  ## Best Practices
530
+
531
+ ### Do's
532
+
533
+ - Use UUID primary keys for public APIs
534
+ - Use DTOs for request/response separation
535
+ - Use Spring Data JPA specifications for complex queries
536
+ - Use @Transactional appropriately
537
+ - Use proper validation annotations
538
+ - Write unit and integration tests
539
+ - Use Spring Security for authentication
540
+ - Use proper exception handling
67
541
  - Use constructor injection
68
- - Use DTOs for API contracts
69
- - Use @Transactional for writes
70
- - Use Spring Security
542
+ - Document APIs with OpenAPI
543
+
544
+ ### Don'ts
545
+
546
+ - Don't expose entities directly in APIs
547
+ - Don't use field injection
548
+ - Don't ignore N+1 query problems
549
+ - Don't skip validation
550
+ - Don't hardcode configuration
551
+ - Don't ignore security headers
552
+ - Don't skip error handling
553
+ - Don't use raw SQL without parameterization
554
+ - Don't forget to handle exceptions globally
555
+ - Don't skip testing
556
+
557
+ ## References
558
+
559
+ - [Spring Boot Documentation](https://spring.io/projects/spring-boot)
560
+ - [Spring Data JPA](https://spring.io/projects/spring-data-jpa)
561
+ - [Spring Security](https://spring.io/projects/spring-security)
562
+ - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide/)
563
+ - [Baeldung Spring Tutorials](https://www.baeldung.com/spring-boot)