@corbat-tech/coding-standards-mcp 1.0.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 (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +371 -0
  3. package/assets/demo.gif +0 -0
  4. package/dist/agent.d.ts +53 -0
  5. package/dist/agent.d.ts.map +1 -0
  6. package/dist/agent.js +629 -0
  7. package/dist/agent.js.map +1 -0
  8. package/dist/cli/init.d.ts +3 -0
  9. package/dist/cli/init.d.ts.map +1 -0
  10. package/dist/cli/init.js +651 -0
  11. package/dist/cli/init.js.map +1 -0
  12. package/dist/config.d.ts +73 -0
  13. package/dist/config.d.ts.map +1 -0
  14. package/dist/config.js +105 -0
  15. package/dist/config.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +73 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/profiles.d.ts +39 -0
  21. package/dist/profiles.d.ts.map +1 -0
  22. package/dist/profiles.js +526 -0
  23. package/dist/profiles.js.map +1 -0
  24. package/dist/prompts-legacy.d.ts +25 -0
  25. package/dist/prompts-legacy.d.ts.map +1 -0
  26. package/dist/prompts-legacy.js +600 -0
  27. package/dist/prompts-legacy.js.map +1 -0
  28. package/dist/prompts-v2.d.ts +30 -0
  29. package/dist/prompts-v2.d.ts.map +1 -0
  30. package/dist/prompts-v2.js +310 -0
  31. package/dist/prompts-v2.js.map +1 -0
  32. package/dist/prompts.d.ts +30 -0
  33. package/dist/prompts.d.ts.map +1 -0
  34. package/dist/prompts.js +310 -0
  35. package/dist/prompts.js.map +1 -0
  36. package/dist/resources.d.ts +18 -0
  37. package/dist/resources.d.ts.map +1 -0
  38. package/dist/resources.js +95 -0
  39. package/dist/resources.js.map +1 -0
  40. package/dist/tools-legacy.d.ts +196 -0
  41. package/dist/tools-legacy.d.ts.map +1 -0
  42. package/dist/tools-legacy.js +1230 -0
  43. package/dist/tools-legacy.js.map +1 -0
  44. package/dist/tools-v2.d.ts +92 -0
  45. package/dist/tools-v2.d.ts.map +1 -0
  46. package/dist/tools-v2.js +410 -0
  47. package/dist/tools-v2.js.map +1 -0
  48. package/dist/tools.d.ts +92 -0
  49. package/dist/tools.d.ts.map +1 -0
  50. package/dist/tools.js +410 -0
  51. package/dist/tools.js.map +1 -0
  52. package/dist/types.d.ts +3054 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +515 -0
  55. package/dist/types.js.map +1 -0
  56. package/dist/utils/index.d.ts +6 -0
  57. package/dist/utils/index.d.ts.map +1 -0
  58. package/dist/utils/index.js +5 -0
  59. package/dist/utils/index.js.map +1 -0
  60. package/dist/utils/retry.d.ts +44 -0
  61. package/dist/utils/retry.d.ts.map +1 -0
  62. package/dist/utils/retry.js +74 -0
  63. package/dist/utils/retry.js.map +1 -0
  64. package/package.json +79 -0
  65. package/profiles/README.md +199 -0
  66. package/profiles/custom/.gitkeep +2 -0
  67. package/profiles/templates/_template.yaml +159 -0
  68. package/profiles/templates/angular.yaml +494 -0
  69. package/profiles/templates/java-spring-backend.yaml +512 -0
  70. package/profiles/templates/minimal.yaml +102 -0
  71. package/profiles/templates/nodejs.yaml +338 -0
  72. package/profiles/templates/python.yaml +340 -0
  73. package/profiles/templates/react.yaml +331 -0
  74. package/profiles/templates/vue.yaml +598 -0
  75. package/standards/architecture/ddd.md +173 -0
  76. package/standards/architecture/hexagonal.md +97 -0
  77. package/standards/cicd/github-actions.md +567 -0
  78. package/standards/clean-code/naming.md +175 -0
  79. package/standards/clean-code/principles.md +179 -0
  80. package/standards/containerization/dockerfile.md +419 -0
  81. package/standards/database/selection-guide.md +443 -0
  82. package/standards/documentation/guidelines.md +189 -0
  83. package/standards/event-driven/domain-events.md +527 -0
  84. package/standards/kubernetes/deployment.md +518 -0
  85. package/standards/observability/guidelines.md +665 -0
  86. package/standards/project-setup/initialization-checklist.md +650 -0
  87. package/standards/spring-boot/best-practices.md +598 -0
  88. package/standards/testing/guidelines.md +559 -0
  89. package/standards/workflow/llm-development-workflow.md +542 -0
@@ -0,0 +1,598 @@
1
+ # Spring Boot Best Practices
2
+
3
+ ## Overview
4
+
5
+ Best practices for Spring Boot 3.x development following modern Java conventions and enterprise patterns.
6
+
7
+ ## Dependency Injection
8
+
9
+ ### Use Constructor Injection
10
+
11
+ ```java
12
+ // Bad - Field injection
13
+ @Service
14
+ public class OrderService {
15
+ @Autowired
16
+ private OrderRepository orderRepository;
17
+ }
18
+
19
+ // Good - Constructor injection
20
+ @Service
21
+ public class OrderService {
22
+ private final OrderRepository orderRepository;
23
+
24
+ public OrderService(OrderRepository orderRepository) {
25
+ this.orderRepository = orderRepository;
26
+ }
27
+ }
28
+
29
+ // Better - Lombok (if using)
30
+ @Service
31
+ @RequiredArgsConstructor
32
+ public class OrderService {
33
+ private final OrderRepository orderRepository;
34
+ }
35
+ ```
36
+
37
+ **Benefits:**
38
+ - Immutable dependencies (final)
39
+ - Explicit dependencies
40
+ - Easy to test
41
+ - Fails fast if dependency missing
42
+
43
+ ## Configuration
44
+
45
+ ### Use @ConfigurationProperties
46
+
47
+ ```java
48
+ // Bad
49
+ @Value("${app.payment.timeout}")
50
+ private int timeout;
51
+
52
+ // Good
53
+ @ConfigurationProperties(prefix = "app.payment")
54
+ public record PaymentProperties(
55
+ int timeout,
56
+ String apiKey,
57
+ boolean retryEnabled,
58
+ RetryProperties retry
59
+ ) {
60
+ public record RetryProperties(
61
+ int maxAttempts,
62
+ Duration backoff
63
+ ) {}
64
+ }
65
+
66
+ @Configuration
67
+ @EnableConfigurationProperties(PaymentProperties.class)
68
+ public class PaymentConfig { }
69
+ ```
70
+
71
+ ### Separate Configuration Classes
72
+
73
+ ```java
74
+ @Configuration
75
+ public class WebConfig implements WebMvcConfigurer {
76
+ // Web-specific configuration
77
+ }
78
+
79
+ @Configuration
80
+ public class SecurityConfig {
81
+ // Security-specific configuration
82
+ }
83
+
84
+ @Configuration
85
+ public class PersistenceConfig {
86
+ // Database-specific configuration
87
+ }
88
+
89
+ @Configuration
90
+ public class KafkaConfig {
91
+ // Messaging-specific configuration
92
+ }
93
+ ```
94
+
95
+ ## HTTP Clients
96
+
97
+ ### When to Use HttpInterface vs RestClient
98
+
99
+ | Scenario | Recommended Client |
100
+ |----------|-------------------|
101
+ | Simple CRUD operations | HttpInterface |
102
+ | Standard REST endpoints | HttpInterface |
103
+ | Complex error handling | RestClient |
104
+ | Dynamic headers/query params | RestClient |
105
+ | Retry policies | RestClient |
106
+ | Circuit breaker integration | RestClient |
107
+
108
+ ### HttpInterface (Declarative - Simple Cases)
109
+
110
+ Best for simple, standard REST API calls with minimal customization.
111
+
112
+ ```java
113
+ // Define the client interface
114
+ @HttpExchange("/api/v1/users")
115
+ public interface UserClient {
116
+
117
+ @GetExchange("/{id}")
118
+ UserResponse getUser(@PathVariable String id);
119
+
120
+ @GetExchange
121
+ List<UserResponse> getAllUsers();
122
+
123
+ @PostExchange
124
+ UserResponse createUser(@RequestBody CreateUserRequest request);
125
+
126
+ @PutExchange("/{id}")
127
+ UserResponse updateUser(@PathVariable String id, @RequestBody UpdateUserRequest request);
128
+
129
+ @DeleteExchange("/{id}")
130
+ void deleteUser(@PathVariable String id);
131
+ }
132
+
133
+ // Configure the client
134
+ @Configuration
135
+ public class UserClientConfig {
136
+
137
+ @Bean
138
+ public UserClient userClient(RestClient.Builder builder) {
139
+ RestClient restClient = builder
140
+ .baseUrl("https://api.example.com")
141
+ .build();
142
+
143
+ HttpServiceProxyFactory factory = HttpServiceProxyFactory
144
+ .builderFor(RestClientAdapter.create(restClient))
145
+ .build();
146
+
147
+ return factory.createClient(UserClient.class);
148
+ }
149
+ }
150
+ ```
151
+
152
+ ### RestClient (Fluent API - Complex Cases)
153
+
154
+ Best for complex scenarios requiring fine-grained control.
155
+
156
+ ```java
157
+ @Component
158
+ @RequiredArgsConstructor
159
+ public class PaymentGatewayClient {
160
+
161
+ private final RestClient restClient;
162
+
163
+ public PaymentResponse processPayment(PaymentRequest request) {
164
+ return restClient.post()
165
+ .uri("/payments")
166
+ .header("X-Idempotency-Key", request.idempotencyKey())
167
+ .header("X-Correlation-Id", MDC.get("correlationId"))
168
+ .contentType(MediaType.APPLICATION_JSON)
169
+ .body(request)
170
+ .retrieve()
171
+ .onStatus(HttpStatusCode::is4xxClientError, this::handleClientError)
172
+ .onStatus(HttpStatusCode::is5xxServerError, this::handleServerError)
173
+ .body(PaymentResponse.class);
174
+ }
175
+
176
+ public Optional<PaymentStatus> getPaymentStatus(String paymentId) {
177
+ return restClient.get()
178
+ .uri("/payments/{id}/status", paymentId)
179
+ .retrieve()
180
+ .onStatus(status -> status.value() == 404, (req, res) -> {})
181
+ .body(new ParameterizedTypeReference<Optional<PaymentStatus>>() {});
182
+ }
183
+
184
+ private void handleClientError(HttpRequest request, ClientHttpResponse response) {
185
+ // Custom error handling logic
186
+ throw new PaymentClientException("Payment failed: " + response.getStatusCode());
187
+ }
188
+
189
+ private void handleServerError(HttpRequest request, ClientHttpResponse response) {
190
+ throw new PaymentGatewayException("Gateway unavailable");
191
+ }
192
+ }
193
+
194
+ // RestClient configuration with interceptors
195
+ @Configuration
196
+ public class RestClientConfig {
197
+
198
+ @Bean
199
+ public RestClient paymentRestClient(RestClient.Builder builder) {
200
+ return builder
201
+ .baseUrl("https://payment.gateway.com/api/v1")
202
+ .defaultHeader("Authorization", "Bearer " + getToken())
203
+ .requestInterceptor(new LoggingInterceptor())
204
+ .requestInterceptor(new RetryInterceptor(3, Duration.ofMillis(500)))
205
+ .build();
206
+ }
207
+ }
208
+ ```
209
+
210
+ ## REST API Design
211
+
212
+ ### Use Problem Details (RFC 7807)
213
+
214
+ ```java
215
+ @RestControllerAdvice
216
+ public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
217
+
218
+ @ExceptionHandler(OrderNotFoundException.class)
219
+ public ProblemDetail handleOrderNotFound(OrderNotFoundException ex) {
220
+ ProblemDetail problem = ProblemDetail.forStatusAndDetail(
221
+ HttpStatus.NOT_FOUND,
222
+ ex.getMessage()
223
+ );
224
+ problem.setTitle("Order Not Found");
225
+ problem.setType(URI.create("https://api.example.com/errors/order-not-found"));
226
+ problem.setProperty("orderId", ex.getOrderId());
227
+ problem.setProperty("timestamp", Instant.now());
228
+ return problem;
229
+ }
230
+
231
+ @ExceptionHandler(BusinessRuleViolationException.class)
232
+ public ProblemDetail handleBusinessRuleViolation(BusinessRuleViolationException ex) {
233
+ ProblemDetail problem = ProblemDetail.forStatusAndDetail(
234
+ HttpStatus.UNPROCESSABLE_ENTITY,
235
+ ex.getMessage()
236
+ );
237
+ problem.setTitle("Business Rule Violation");
238
+ problem.setType(URI.create("https://api.example.com/errors/business-rule-violation"));
239
+ problem.setProperty("rule", ex.getRuleName());
240
+ return problem;
241
+ }
242
+
243
+ @ExceptionHandler(ConstraintViolationException.class)
244
+ public ProblemDetail handleValidation(ConstraintViolationException ex) {
245
+ ProblemDetail problem = ProblemDetail.forStatusAndDetail(
246
+ HttpStatus.BAD_REQUEST,
247
+ "Validation failed"
248
+ );
249
+ problem.setTitle("Validation Error");
250
+ problem.setProperty("violations", ex.getConstraintViolations().stream()
251
+ .map(v -> Map.of(
252
+ "field", v.getPropertyPath().toString(),
253
+ "message", v.getMessage()
254
+ ))
255
+ .toList());
256
+ return problem;
257
+ }
258
+ }
259
+ ```
260
+
261
+ ### Use DTOs for API
262
+
263
+ ```java
264
+ // Request DTO with validation
265
+ public record CreateOrderRequest(
266
+ @NotNull(message = "Customer ID is required")
267
+ String customerId,
268
+
269
+ @NotEmpty(message = "Order must have at least one line")
270
+ @Size(max = 100, message = "Order cannot have more than 100 lines")
271
+ List<@Valid OrderLineRequest> lines,
272
+
273
+ @Email(message = "Invalid email format")
274
+ String contactEmail
275
+ ) {
276
+ public CreateOrderCommand toCommand() {
277
+ return new CreateOrderCommand(
278
+ CustomerId.of(customerId),
279
+ lines.stream().map(OrderLineRequest::toLine).toList()
280
+ );
281
+ }
282
+ }
283
+
284
+ // Response DTO
285
+ public record OrderResponse(
286
+ String id,
287
+ String status,
288
+ BigDecimal total,
289
+ List<OrderLineResponse> lines,
290
+ Instant createdAt
291
+ ) {
292
+ public static OrderResponse from(Order order) {
293
+ return new OrderResponse(
294
+ order.getId().value(),
295
+ order.getStatus().name(),
296
+ order.getTotal().amount(),
297
+ order.getLines().stream().map(OrderLineResponse::from).toList(),
298
+ order.getCreatedAt()
299
+ );
300
+ }
301
+ }
302
+ ```
303
+
304
+ ### Controller Structure
305
+
306
+ ```java
307
+ @RestController
308
+ @RequestMapping("/api/v1/orders")
309
+ @RequiredArgsConstructor
310
+ @Tag(name = "Orders", description = "Order management endpoints")
311
+ public class OrderController {
312
+
313
+ private final PlaceOrderUseCase placeOrderUseCase;
314
+ private final GetOrderUseCase getOrderUseCase;
315
+ private final ListOrdersUseCase listOrdersUseCase;
316
+
317
+ @PostMapping
318
+ @ResponseStatus(HttpStatus.CREATED)
319
+ @Operation(summary = "Create a new order")
320
+ @ApiResponse(responseCode = "201", description = "Order created successfully")
321
+ @ApiResponse(responseCode = "400", description = "Invalid request")
322
+ public OrderResponse createOrder(@Valid @RequestBody CreateOrderRequest request) {
323
+ Order order = placeOrderUseCase.execute(request.toCommand());
324
+ return OrderResponse.from(order);
325
+ }
326
+
327
+ @GetMapping("/{id}")
328
+ @Operation(summary = "Get order by ID")
329
+ @ApiResponse(responseCode = "200", description = "Order found")
330
+ @ApiResponse(responseCode = "404", description = "Order not found")
331
+ public OrderResponse getOrder(@PathVariable String id) {
332
+ return getOrderUseCase.execute(OrderId.of(id))
333
+ .map(OrderResponse::from)
334
+ .orElseThrow(() -> new OrderNotFoundException(id));
335
+ }
336
+
337
+ @GetMapping
338
+ @Operation(summary = "List orders with pagination")
339
+ public Page<OrderResponse> listOrders(
340
+ @RequestParam(defaultValue = "0") int page,
341
+ @RequestParam(defaultValue = "20") int size) {
342
+ return listOrdersUseCase.execute(PageRequest.of(page, size))
343
+ .map(OrderResponse::from);
344
+ }
345
+ }
346
+ ```
347
+
348
+ ## Validation
349
+
350
+ ### Use Bean Validation
351
+
352
+ ```java
353
+ public record CreateOrderRequest(
354
+ @NotNull(message = "Customer ID is required")
355
+ String customerId,
356
+
357
+ @NotEmpty(message = "Order must have at least one line")
358
+ @Size(max = 100, message = "Order cannot have more than 100 lines")
359
+ List<@Valid OrderLineRequest> lines,
360
+
361
+ @Email(message = "Invalid email format")
362
+ String contactEmail,
363
+
364
+ @Pattern(regexp = "^[A-Z]{2}$", message = "Country code must be 2 uppercase letters")
365
+ String countryCode
366
+ ) { }
367
+ ```
368
+
369
+ ### Custom Validators
370
+
371
+ ```java
372
+ @Constraint(validatedBy = ValidOrderIdValidator.class)
373
+ @Target({ElementType.FIELD, ElementType.PARAMETER})
374
+ @Retention(RetentionPolicy.RUNTIME)
375
+ public @interface ValidOrderId {
376
+ String message() default "Invalid order ID format";
377
+ Class<?>[] groups() default {};
378
+ Class<? extends Payload>[] payload() default {};
379
+ }
380
+
381
+ public class ValidOrderIdValidator implements ConstraintValidator<ValidOrderId, String> {
382
+ private static final Pattern ORDER_ID_PATTERN = Pattern.compile("^ORD-[A-Z0-9]{8}$");
383
+
384
+ @Override
385
+ public boolean isValid(String value, ConstraintValidatorContext context) {
386
+ if (value == null) return true; // Use @NotNull for null checks
387
+ return ORDER_ID_PATTERN.matcher(value).matches();
388
+ }
389
+ }
390
+ ```
391
+
392
+ ## Persistence
393
+
394
+ ### Entity Design
395
+
396
+ ```java
397
+ @Entity
398
+ @Table(name = "orders")
399
+ public class OrderJpaEntity {
400
+
401
+ @Id
402
+ private String id;
403
+
404
+ @Column(nullable = false)
405
+ private String customerId;
406
+
407
+ @Enumerated(EnumType.STRING)
408
+ @Column(nullable = false)
409
+ private OrderStatus status;
410
+
411
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
412
+ @JoinColumn(name = "order_id")
413
+ private List<OrderLineJpaEntity> lines = new ArrayList<>();
414
+
415
+ @Column(nullable = false, updatable = false)
416
+ private Instant createdAt;
417
+
418
+ @Column(nullable = false)
419
+ private Instant updatedAt;
420
+
421
+ // Domain entity conversion
422
+ public Order toDomain() {
423
+ return Order.reconstitute(
424
+ OrderId.of(id),
425
+ CustomerId.of(customerId),
426
+ status,
427
+ lines.stream().map(OrderLineJpaEntity::toDomain).toList(),
428
+ createdAt
429
+ );
430
+ }
431
+
432
+ public static OrderJpaEntity fromDomain(Order order) {
433
+ OrderJpaEntity entity = new OrderJpaEntity();
434
+ entity.id = order.getId().value();
435
+ entity.customerId = order.getCustomerId().value();
436
+ entity.status = order.getStatus();
437
+ entity.lines = order.getLines().stream()
438
+ .map(OrderLineJpaEntity::fromDomain)
439
+ .toList();
440
+ entity.createdAt = order.getCreatedAt();
441
+ entity.updatedAt = Instant.now();
442
+ return entity;
443
+ }
444
+ }
445
+ ```
446
+
447
+ ### Repository Pattern
448
+
449
+ ```java
450
+ // Domain port (in domain layer)
451
+ public interface OrderRepository {
452
+ Optional<Order> findById(OrderId id);
453
+ List<Order> findByCustomerId(CustomerId customerId);
454
+ void save(Order order);
455
+ void delete(OrderId id);
456
+ }
457
+
458
+ // JPA Repository (in infrastructure layer)
459
+ public interface OrderJpaRepository extends JpaRepository<OrderJpaEntity, String> {
460
+ List<OrderJpaEntity> findByCustomerId(String customerId);
461
+ @Query("SELECT o FROM OrderJpaEntity o WHERE o.status = :status")
462
+ List<OrderJpaEntity> findByStatus(@Param("status") OrderStatus status);
463
+ }
464
+
465
+ // Infrastructure adapter (implements domain port)
466
+ @Repository
467
+ @RequiredArgsConstructor
468
+ public class JpaOrderRepository implements OrderRepository {
469
+
470
+ private final OrderJpaRepository jpaRepository;
471
+
472
+ @Override
473
+ public Optional<Order> findById(OrderId id) {
474
+ return jpaRepository.findById(id.value())
475
+ .map(OrderJpaEntity::toDomain);
476
+ }
477
+
478
+ @Override
479
+ public List<Order> findByCustomerId(CustomerId customerId) {
480
+ return jpaRepository.findByCustomerId(customerId.value())
481
+ .stream()
482
+ .map(OrderJpaEntity::toDomain)
483
+ .toList();
484
+ }
485
+
486
+ @Override
487
+ @Transactional
488
+ public void save(Order order) {
489
+ jpaRepository.save(OrderJpaEntity.fromDomain(order));
490
+ }
491
+
492
+ @Override
493
+ @Transactional
494
+ public void delete(OrderId id) {
495
+ jpaRepository.deleteById(id.value());
496
+ }
497
+ }
498
+ ```
499
+
500
+ ## Object Mapping with MapStruct
501
+
502
+ ```java
503
+ @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
504
+ public interface OrderMapper {
505
+
506
+ @Mapping(target = "id", source = "id.value")
507
+ @Mapping(target = "customerId", source = "customerId.value")
508
+ @Mapping(target = "total", source = "total.amount")
509
+ OrderResponse toResponse(Order order);
510
+
511
+ @Mapping(target = "id", expression = "java(OrderId.of(entity.getId()))")
512
+ @Mapping(target = "customerId", expression = "java(CustomerId.of(entity.getCustomerId()))")
513
+ Order toDomain(OrderJpaEntity entity);
514
+
515
+ @InheritInverseConfiguration
516
+ OrderJpaEntity toEntity(Order order);
517
+ }
518
+ ```
519
+
520
+ ## Slice Tests
521
+
522
+ ### Web Layer Only
523
+
524
+ ```java
525
+ @WebMvcTest(OrderController.class)
526
+ class OrderControllerTest {
527
+ @Autowired MockMvc mockMvc;
528
+ @MockBean PlaceOrderUseCase placeOrderUseCase;
529
+ @MockBean GetOrderUseCase getOrderUseCase;
530
+ }
531
+ ```
532
+
533
+ ### Repository Layer Only
534
+
535
+ ```java
536
+ @DataJpaTest
537
+ class OrderJpaRepositoryTest {
538
+ @Autowired TestEntityManager entityManager;
539
+ @Autowired OrderJpaRepository repository;
540
+ }
541
+ ```
542
+
543
+ ## Security Practices
544
+
545
+ 1. Never store secrets in code or properties files
546
+ 2. Use environment variables or secret managers (AWS Secrets Manager, HashiCorp Vault)
547
+ 3. Enable HTTPS in production
548
+ 4. Validate and sanitize all input
549
+ 5. Use parameterized queries (JPA handles this)
550
+ 6. Implement proper CORS configuration
551
+ 7. Use Spring Security for authentication/authorization
552
+ 8. Set security headers (CSP, X-Frame-Options, X-Content-Type-Options)
553
+ 9. Implement rate limiting for public APIs
554
+ 10. Use JWT with proper expiration and refresh tokens
555
+
556
+ ## Application Properties Best Practices
557
+
558
+ ```yaml
559
+ # application.yml
560
+ spring:
561
+ application:
562
+ name: order-service
563
+
564
+ datasource:
565
+ url: ${DATABASE_URL}
566
+ username: ${DATABASE_USERNAME}
567
+ password: ${DATABASE_PASSWORD}
568
+
569
+ jpa:
570
+ hibernate:
571
+ ddl-auto: validate # Never use create/update in production
572
+ open-in-view: false # Disable OSIV
573
+
574
+ jackson:
575
+ default-property-inclusion: non_null
576
+ serialization:
577
+ write-dates-as-timestamps: false
578
+
579
+ management:
580
+ endpoints:
581
+ web:
582
+ exposure:
583
+ include: health,info,metrics,prometheus
584
+ endpoint:
585
+ health:
586
+ show-details: when_authorized
587
+ probes:
588
+ enabled: true
589
+
590
+ server:
591
+ shutdown: graceful
592
+ tomcat:
593
+ connection-timeout: 5s
594
+ max-connections: 10000
595
+ threads:
596
+ max: 200
597
+ min-spare: 10
598
+ ```