oh-my-customcode 0.9.2 → 0.9.4
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.
- package/README.md +25 -30
- package/dist/cli/index.js +1315 -568
- package/dist/index.js +234 -17
- package/package.json +1 -1
- package/templates/.claude/agents/arch-documenter.md +7 -71
- package/templates/.claude/agents/arch-speckit-agent.md +21 -108
- package/templates/.claude/agents/be-express-expert.md +8 -58
- package/templates/.claude/agents/be-nestjs-expert.md +6 -38
- package/templates/.claude/agents/be-springboot-expert.md +11 -56
- package/templates/.claude/agents/db-postgres-expert.md +10 -80
- package/templates/.claude/agents/db-redis-expert.md +10 -75
- package/templates/.claude/agents/db-supabase-expert.md +12 -48
- package/templates/.claude/agents/de-airflow-expert.md +8 -45
- package/templates/.claude/agents/de-dbt-expert.md +8 -46
- package/templates/.claude/agents/de-kafka-expert.md +10 -10
- package/templates/.claude/agents/de-pipeline-expert.md +9 -69
- package/templates/.claude/agents/de-snowflake-expert.md +9 -62
- package/templates/.claude/agents/de-spark-expert.md +10 -54
- package/templates/.claude/agents/fe-svelte-agent.md +5 -41
- package/templates/.claude/agents/fe-vercel-agent.md +9 -41
- package/templates/.claude/agents/fe-vuejs-agent.md +7 -42
- package/templates/.claude/agents/lang-java21-expert.md +11 -37
- package/templates/.claude/agents/mgr-claude-code-bible.md +22 -207
- package/templates/.claude/agents/mgr-creator.md +7 -88
- package/templates/.claude/agents/mgr-gitnerd.md +8 -76
- package/templates/.claude/agents/mgr-sauron.md +27 -20
- package/templates/.claude/agents/mgr-supplier.md +11 -96
- package/templates/.claude/agents/mgr-sync-checker.md +9 -70
- package/templates/.claude/agents/mgr-updater.md +9 -79
- package/templates/.claude/agents/qa-engineer.md +8 -72
- package/templates/.claude/agents/qa-planner.md +2 -3
- package/templates/.claude/agents/qa-writer.md +6 -76
- package/templates/.claude/agents/sys-memory-keeper.md +13 -87
- package/templates/.claude/agents/sys-naggy.md +9 -62
- package/templates/.claude/agents/tool-bun-expert.md +7 -52
- package/templates/.claude/agents/tool-npm-expert.md +6 -64
- package/templates/.claude/agents/tool-optimizer.md +7 -60
- package/templates/.claude/rules/MAY-optimization.md +16 -80
- package/templates/.claude/rules/MUST-agent-design.md +29 -134
- package/templates/.claude/rules/MUST-agent-identification.md +9 -88
- package/templates/.claude/rules/MUST-continuous-improvement.md +10 -117
- package/templates/.claude/rules/MUST-intent-transparency.md +14 -171
- package/templates/.claude/rules/MUST-language-policy.md +11 -46
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +82 -425
- package/templates/.claude/rules/MUST-parallel-execution.md +33 -405
- package/templates/.claude/rules/MUST-permissions.md +14 -68
- package/templates/.claude/rules/MUST-safety.md +11 -57
- package/templates/.claude/rules/MUST-sync-verification.md +49 -205
- package/templates/.claude/rules/MUST-tool-identification.md +21 -134
- package/templates/.claude/rules/SHOULD-agent-teams.md +22 -166
- package/templates/.claude/rules/SHOULD-ecomode.md +15 -123
- package/templates/.claude/rules/SHOULD-error-handling.md +19 -88
- package/templates/.claude/rules/SHOULD-hud-statusline.md +9 -89
- package/templates/.claude/rules/SHOULD-interaction.md +18 -87
- package/templates/.claude/rules/SHOULD-memory-integration.md +25 -118
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +70 -243
- package/templates/.claude/skills/springboot-best-practices/SKILL.md +180 -319
- package/templates/.codex/agents/arch-documenter.md +7 -71
- package/templates/.codex/agents/arch-speckit-agent.md +21 -108
- package/templates/.codex/agents/be-express-expert.md +8 -58
- package/templates/.codex/agents/be-nestjs-expert.md +6 -38
- package/templates/.codex/agents/be-springboot-expert.md +11 -56
- package/templates/.codex/agents/db-postgres-expert.md +10 -80
- package/templates/.codex/agents/db-redis-expert.md +10 -75
- package/templates/.codex/agents/db-supabase-expert.md +12 -48
- package/templates/.codex/agents/de-airflow-expert.md +8 -45
- package/templates/.codex/agents/de-dbt-expert.md +8 -46
- package/templates/.codex/agents/de-kafka-expert.md +10 -10
- package/templates/.codex/agents/de-pipeline-expert.md +9 -69
- package/templates/.codex/agents/de-snowflake-expert.md +9 -62
- package/templates/.codex/agents/de-spark-expert.md +10 -54
- package/templates/.codex/agents/fe-svelte-agent.md +5 -41
- package/templates/.codex/agents/fe-vercel-agent.md +9 -41
- package/templates/.codex/agents/fe-vuejs-agent.md +7 -42
- package/templates/.codex/agents/infra-aws-expert.md +1 -1
- package/templates/.codex/agents/infra-docker-expert.md +1 -1
- package/templates/.codex/agents/lang-java21-expert.md +11 -37
- package/templates/.codex/agents/mgr-claude-code-bible.md +25 -210
- package/templates/.codex/agents/mgr-creator.md +7 -88
- package/templates/.codex/agents/mgr-gitnerd.md +8 -76
- package/templates/.codex/agents/mgr-sauron.md +30 -23
- package/templates/.codex/agents/mgr-supplier.md +11 -96
- package/templates/.codex/agents/mgr-sync-checker.md +12 -73
- package/templates/.codex/agents/mgr-updater.md +9 -79
- package/templates/.codex/agents/qa-engineer.md +8 -72
- package/templates/.codex/agents/qa-planner.md +2 -3
- package/templates/.codex/agents/qa-writer.md +6 -76
- package/templates/.codex/agents/sys-memory-keeper.md +13 -87
- package/templates/.codex/agents/sys-naggy.md +9 -62
- package/templates/.codex/agents/tool-bun-expert.md +7 -52
- package/templates/.codex/agents/tool-npm-expert.md +6 -64
- package/templates/.codex/agents/tool-optimizer.md +7 -60
- package/templates/.codex/rules/MAY-optimization.md +16 -80
- package/templates/.codex/rules/MUST-agent-design.md +29 -134
- package/templates/.codex/rules/MUST-agent-identification.md +9 -88
- package/templates/.codex/rules/MUST-continuous-improvement.md +10 -117
- package/templates/.codex/rules/MUST-intent-transparency.md +14 -171
- package/templates/.codex/rules/MUST-language-policy.md +11 -46
- package/templates/.codex/rules/MUST-orchestrator-coordination.md +82 -425
- package/templates/.codex/rules/MUST-parallel-execution.md +33 -405
- package/templates/.codex/rules/MUST-permissions.md +14 -68
- package/templates/.codex/rules/MUST-safety.md +11 -57
- package/templates/.codex/rules/MUST-sync-verification.md +58 -214
- package/templates/.codex/rules/MUST-tool-identification.md +22 -135
- package/templates/.codex/rules/SHOULD-agent-teams.md +22 -166
- package/templates/.codex/rules/SHOULD-ecomode.md +15 -123
- package/templates/.codex/rules/SHOULD-error-handling.md +19 -88
- package/templates/.codex/rules/SHOULD-hud-statusline.md +9 -89
- package/templates/.codex/rules/SHOULD-interaction.md +18 -87
- package/templates/.codex/rules/SHOULD-memory-integration.md +25 -118
- package/templates/.codex/skills/dev-lead-routing/SKILL.md +70 -243
- package/templates/.codex/skills/springboot-best-practices/SKILL.md +180 -319
|
@@ -4,354 +4,215 @@ description: Spring Boot patterns for enterprise Java applications
|
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## Purpose
|
|
8
|
-
|
|
9
|
-
Apply Spring Boot patterns for building enterprise-grade applications.
|
|
10
|
-
|
|
11
7
|
## Rules
|
|
12
8
|
|
|
13
9
|
### 1. Project Structure
|
|
14
|
-
|
|
15
|
-
```yaml
|
|
16
|
-
structure:
|
|
17
|
-
layout: layered architecture
|
|
18
|
-
packages:
|
|
19
|
-
- controller: REST endpoints
|
|
20
|
-
- service: Business logic
|
|
21
|
-
- repository: Data access
|
|
22
|
-
- model/entity: Domain objects
|
|
23
|
-
- dto: Data transfer objects
|
|
24
|
-
- config: Configuration classes
|
|
25
|
-
- exception: Custom exceptions
|
|
26
|
-
|
|
27
|
-
example: |
|
|
28
|
-
com.example.app/
|
|
29
|
-
├── controller/
|
|
30
|
-
│ └── UserController.java
|
|
31
|
-
├── service/
|
|
32
|
-
│ ├── UserService.java
|
|
33
|
-
│ └── impl/UserServiceImpl.java
|
|
34
|
-
├── repository/
|
|
35
|
-
│ └── UserRepository.java
|
|
36
|
-
├── model/
|
|
37
|
-
│ └── User.java
|
|
38
|
-
├── dto/
|
|
39
|
-
│ ├── UserRequest.java
|
|
40
|
-
│ └── UserResponse.java
|
|
41
|
-
├── config/
|
|
42
|
-
│ └── SecurityConfig.java
|
|
43
|
-
└── exception/
|
|
44
|
-
└── UserNotFoundException.java
|
|
45
|
-
```
|
|
10
|
+
Layered architecture: controller (REST), service (business logic), repository (data access), model/entity, dto, config, exception.
|
|
46
11
|
|
|
47
12
|
### 2. Dependency Injection
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
@RequiredArgsConstructor
|
|
59
|
-
public class UserService {
|
|
60
|
-
private final UserRepository userRepository;
|
|
61
|
-
private final EmailService emailService;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// AVOID: Field injection
|
|
65
|
-
@Service
|
|
66
|
-
public class UserService {
|
|
67
|
-
@Autowired
|
|
68
|
-
private UserRepository userRepository; // Not recommended
|
|
69
|
-
}
|
|
13
|
+
Constructor injection preferred. Use @RequiredArgsConstructor with final fields. Avoid field injection with @Autowired.
|
|
14
|
+
|
|
15
|
+
```java
|
|
16
|
+
// GOOD: Constructor injection
|
|
17
|
+
@Service
|
|
18
|
+
@RequiredArgsConstructor
|
|
19
|
+
public class UserService {
|
|
20
|
+
private final UserRepository userRepository;
|
|
21
|
+
private final EmailService emailService;
|
|
22
|
+
}
|
|
70
23
|
```
|
|
71
24
|
|
|
72
25
|
### 3. REST API Design
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
@PostMapping
|
|
95
|
-
@ResponseStatus(HttpStatus.CREATED)
|
|
96
|
-
public UserResponse createUser(@Valid @RequestBody UserRequest request) {
|
|
97
|
-
return userService.create(request);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
@PutMapping("/{id}")
|
|
101
|
-
public UserResponse updateUser(
|
|
102
|
-
@PathVariable Long id,
|
|
103
|
-
@Valid @RequestBody UserRequest request
|
|
104
|
-
) {
|
|
105
|
-
return userService.update(id, request);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
@DeleteMapping("/{id}")
|
|
109
|
-
@ResponseStatus(HttpStatus.NO_CONTENT)
|
|
110
|
-
public void deleteUser(@PathVariable Long id) {
|
|
111
|
-
userService.delete(id);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
26
|
+
@RestController + @RequestMapping. Use @Validated for input, ResponseEntity for responses, proper HTTP status codes.
|
|
27
|
+
|
|
28
|
+
```java
|
|
29
|
+
@RestController
|
|
30
|
+
@RequestMapping("/api/v1/users")
|
|
31
|
+
@RequiredArgsConstructor
|
|
32
|
+
public class UserController {
|
|
33
|
+
private final UserService userService;
|
|
34
|
+
|
|
35
|
+
@GetMapping("/{id}")
|
|
36
|
+
public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
|
|
37
|
+
return ResponseEntity.ok(userService.findById(id));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@PostMapping
|
|
41
|
+
@ResponseStatus(HttpStatus.CREATED)
|
|
42
|
+
public UserResponse createUser(@Valid @RequestBody UserRequest request) {
|
|
43
|
+
return userService.create(request);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
114
46
|
```
|
|
115
47
|
|
|
116
48
|
### 4. Service Layer
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
User user = userRepository.findById(id)
|
|
141
|
-
.orElseThrow(() -> new UserNotFoundException(id));
|
|
142
|
-
return userMapper.toResponse(user);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
@Override
|
|
146
|
-
@Transactional
|
|
147
|
-
public UserResponse create(UserRequest request) {
|
|
148
|
-
User user = userMapper.toEntity(request);
|
|
149
|
-
return userMapper.toResponse(userRepository.save(user));
|
|
150
|
-
}
|
|
151
|
-
}
|
|
49
|
+
Business logic in services. @Transactional boundaries at service level. Interface + implementation pattern.
|
|
50
|
+
|
|
51
|
+
```java
|
|
52
|
+
@Service
|
|
53
|
+
@Transactional(readOnly = true)
|
|
54
|
+
@RequiredArgsConstructor
|
|
55
|
+
public class UserServiceImpl implements UserService {
|
|
56
|
+
private final UserRepository userRepository;
|
|
57
|
+
|
|
58
|
+
@Override
|
|
59
|
+
public UserResponse findById(Long id) {
|
|
60
|
+
User user = userRepository.findById(id)
|
|
61
|
+
.orElseThrow(() -> new UserNotFoundException(id));
|
|
62
|
+
return userMapper.toResponse(user);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Override
|
|
66
|
+
@Transactional
|
|
67
|
+
public UserResponse create(UserRequest request) {
|
|
68
|
+
User user = userMapper.toEntity(request);
|
|
69
|
+
return userMapper.toResponse(userRepository.save(user));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
152
72
|
```
|
|
153
73
|
|
|
154
74
|
### 5. Data Access
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
@Id
|
|
182
|
-
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
183
|
-
private Long id;
|
|
184
|
-
|
|
185
|
-
@Column(nullable = false, unique = true)
|
|
186
|
-
private String email;
|
|
187
|
-
|
|
188
|
-
@Enumerated(EnumType.STRING)
|
|
189
|
-
private UserStatus status;
|
|
190
|
-
|
|
191
|
-
@CreatedDate
|
|
192
|
-
private LocalDateTime createdAt;
|
|
193
|
-
}
|
|
75
|
+
Spring Data JPA. @Query or method naming for custom queries. @Entity with proper JPA annotations.
|
|
76
|
+
|
|
77
|
+
```java
|
|
78
|
+
public interface UserRepository extends JpaRepository<User, Long> {
|
|
79
|
+
Optional<User> findByEmail(String email);
|
|
80
|
+
|
|
81
|
+
@Query("SELECT u FROM User u WHERE u.status = :status")
|
|
82
|
+
List<User> findByStatus(@Param("status") UserStatus status);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Entity
|
|
86
|
+
@Table(name = "users")
|
|
87
|
+
@Getter
|
|
88
|
+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
|
89
|
+
public class User {
|
|
90
|
+
@Id
|
|
91
|
+
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
92
|
+
private Long id;
|
|
93
|
+
|
|
94
|
+
@Column(nullable = false, unique = true)
|
|
95
|
+
private String email;
|
|
96
|
+
|
|
97
|
+
@Enumerated(EnumType.STRING)
|
|
98
|
+
private UserStatus status;
|
|
99
|
+
}
|
|
194
100
|
```
|
|
195
101
|
|
|
196
102
|
### 6. Exception Handling
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
.map(e -> e.getField() + ": " + e.getDefaultMessage())
|
|
220
|
-
.toList();
|
|
221
|
-
return new ErrorResponse("VALIDATION_ERROR", errors);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
103
|
+
@RestControllerAdvice for global handling. Domain-specific exceptions with proper HTTP status mapping.
|
|
104
|
+
|
|
105
|
+
```java
|
|
106
|
+
@RestControllerAdvice
|
|
107
|
+
public class GlobalExceptionHandler {
|
|
108
|
+
@ExceptionHandler(UserNotFoundException.class)
|
|
109
|
+
@ResponseStatus(HttpStatus.NOT_FOUND)
|
|
110
|
+
public ErrorResponse handleUserNotFound(UserNotFoundException ex) {
|
|
111
|
+
return new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@ExceptionHandler(MethodArgumentNotValidException.class)
|
|
115
|
+
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
|
116
|
+
public ErrorResponse handleValidation(MethodArgumentNotValidException ex) {
|
|
117
|
+
List<String> errors = ex.getBindingResult()
|
|
118
|
+
.getFieldErrors()
|
|
119
|
+
.stream()
|
|
120
|
+
.map(e -> e.getField() + ": " + e.getDefaultMessage())
|
|
121
|
+
.toList();
|
|
122
|
+
return new ErrorResponse("VALIDATION_ERROR", errors);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
224
125
|
```
|
|
225
126
|
|
|
226
127
|
### 7. Configuration
|
|
128
|
+
Profile-based: application-{profile}.yml. @ConfigurationProperties for type-safe config. Externalize sensitive values.
|
|
227
129
|
|
|
228
130
|
```yaml
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
active: ${SPRING_PROFILES_ACTIVE:local}
|
|
238
|
-
datasource:
|
|
239
|
-
url: ${DATABASE_URL}
|
|
240
|
-
username: ${DATABASE_USERNAME}
|
|
241
|
-
password: ${DATABASE_PASSWORD}
|
|
242
|
-
|
|
243
|
-
# application-local.yml
|
|
244
|
-
spring:
|
|
245
|
-
datasource:
|
|
246
|
-
url: jdbc:h2:mem:testdb
|
|
247
|
-
driver-class-name: org.h2.Driver
|
|
248
|
-
|
|
249
|
-
configuration_class: |
|
|
250
|
-
@Configuration
|
|
251
|
-
@ConfigurationProperties(prefix = "app")
|
|
252
|
-
@Validated
|
|
253
|
-
public class AppProperties {
|
|
254
|
-
|
|
255
|
-
@NotBlank
|
|
256
|
-
private String name;
|
|
257
|
-
|
|
258
|
-
@Min(1)
|
|
259
|
-
private int maxConnections;
|
|
260
|
-
|
|
261
|
-
// getters, setters
|
|
262
|
-
}
|
|
131
|
+
# application.yml
|
|
132
|
+
spring:
|
|
133
|
+
profiles:
|
|
134
|
+
active: ${SPRING_PROFILES_ACTIVE:local}
|
|
135
|
+
datasource:
|
|
136
|
+
url: ${DATABASE_URL}
|
|
137
|
+
username: ${DATABASE_USERNAME}
|
|
138
|
+
password: ${DATABASE_PASSWORD}
|
|
263
139
|
```
|
|
264
140
|
|
|
265
|
-
|
|
141
|
+
```java
|
|
142
|
+
@Configuration
|
|
143
|
+
@ConfigurationProperties(prefix = "app")
|
|
144
|
+
@Validated
|
|
145
|
+
public class AppProperties {
|
|
146
|
+
@NotBlank
|
|
147
|
+
private String name;
|
|
148
|
+
|
|
149
|
+
@Min(1)
|
|
150
|
+
private int maxConnections;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
266
153
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
.requestMatchers("/api/v1/auth/**").permitAll()
|
|
289
|
-
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
|
|
290
|
-
.anyRequest().authenticated())
|
|
291
|
-
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
|
|
292
|
-
.build();
|
|
293
|
-
}
|
|
294
|
-
}
|
|
154
|
+
### 8. Security
|
|
155
|
+
Spring Security with SecurityFilterChain. Externalize secrets. Proper authentication/authorization patterns.
|
|
156
|
+
|
|
157
|
+
```java
|
|
158
|
+
@Configuration
|
|
159
|
+
@EnableWebSecurity
|
|
160
|
+
@RequiredArgsConstructor
|
|
161
|
+
public class SecurityConfig {
|
|
162
|
+
@Bean
|
|
163
|
+
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
|
164
|
+
return http
|
|
165
|
+
.csrf(csrf -> csrf.disable())
|
|
166
|
+
.sessionManagement(session ->
|
|
167
|
+
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
|
168
|
+
.authorizeHttpRequests(auth -> auth
|
|
169
|
+
.requestMatchers("/api/v1/auth/**").permitAll()
|
|
170
|
+
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
|
|
171
|
+
.anyRequest().authenticated())
|
|
172
|
+
.build();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
295
175
|
```
|
|
296
176
|
|
|
297
177
|
### 9. Testing
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
@Test
|
|
335
|
-
void findByEmail_shouldReturnUser() {
|
|
336
|
-
User user = userRepository.save(new User("test@example.com"));
|
|
337
|
-
|
|
338
|
-
Optional<User> found = userRepository.findByEmail("test@example.com");
|
|
339
|
-
|
|
340
|
-
assertThat(found).isPresent();
|
|
341
|
-
assertThat(found.get().getEmail()).isEqualTo("test@example.com");
|
|
342
|
-
}
|
|
343
|
-
}
|
|
178
|
+
@WebMvcTest (controller), @DataJpaTest (repository), @SpringBootTest (integration), @MockBean for mocking.
|
|
179
|
+
|
|
180
|
+
```java
|
|
181
|
+
// Controller test
|
|
182
|
+
@WebMvcTest(UserController.class)
|
|
183
|
+
class UserControllerTest {
|
|
184
|
+
@Autowired
|
|
185
|
+
private MockMvc mockMvc;
|
|
186
|
+
|
|
187
|
+
@MockBean
|
|
188
|
+
private UserService userService;
|
|
189
|
+
|
|
190
|
+
@Test
|
|
191
|
+
void getUser_shouldReturnUser() throws Exception {
|
|
192
|
+
given(userService.findById(1L))
|
|
193
|
+
.willReturn(new UserResponse(1L, "test@example.com"));
|
|
194
|
+
|
|
195
|
+
mockMvc.perform(get("/api/v1/users/1"))
|
|
196
|
+
.andExpect(status().isOk())
|
|
197
|
+
.andExpect(jsonPath("$.email").value("test@example.com"));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Repository test
|
|
202
|
+
@DataJpaTest
|
|
203
|
+
class UserRepositoryTest {
|
|
204
|
+
@Autowired
|
|
205
|
+
private UserRepository userRepository;
|
|
206
|
+
|
|
207
|
+
@Test
|
|
208
|
+
void findByEmail_shouldReturnUser() {
|
|
209
|
+
User user = userRepository.save(new User("test@example.com"));
|
|
210
|
+
Optional<User> found = userRepository.findByEmail("test@example.com");
|
|
211
|
+
assertThat(found).isPresent();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
344
214
|
```
|
|
345
215
|
|
|
346
216
|
## Application
|
|
347
217
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
1. **Always** use constructor injection
|
|
351
|
-
2. **Always** use layered architecture
|
|
352
|
-
3. **Prefer** interface-based services
|
|
353
|
-
4. **Use** DTOs for API contracts
|
|
354
|
-
5. **Handle** exceptions globally
|
|
355
|
-
6. **Externalize** configuration
|
|
356
|
-
7. **Secure** endpoints properly
|
|
357
|
-
8. **Test** each layer appropriately
|
|
218
|
+
Always: constructor injection, layered architecture, DTOs, global exception handling, externalized config, proper security, layer-appropriate tests.
|