Configuration-Driven REST Framework Documentation
Table of Contents
Core Components
EndpointConfig.java
@Data@NoArgsConstructor@AllArgsConstructorpublic class EndpointConfig { @NotBlank(message = "Path is required") @Pattern(regexp = "^/.*", message = "Path must start with /") private String path;
@NotNull(message = "HTTP method is required") @Pattern(regexp = "^(GET|POST|PUT|DELETE|PATCH)$", message = "Invalid HTTP method") private String httpMethod;
@NotEmpty(message = "At least one access right is required") private List<String> requiredRights;
@NotBlank(message = "Input transformer is required") private String inputTransformer;
@NotBlank(message = "Business logic bean is required") private String businessLogicBean;
@NotBlank(message = "Output transformer is required") private String outputTransformer;
@Valid private ValidationRules validationRules;}
This is the core configuration class that defines the structure of each endpoint. The annotations provide validation to ensure all required fields are properly set.
ValidationRules.java
@Datapublic class ValidationRules { private RateLimitRule rateLimit; private Long maxRequestSizeBytes; private List<String> allowedContentTypes; private Map<String, String> customValidations;}
Defines validation rules that can be applied to any endpoint through configuration.
Examples
1. Order Processing System
OrderController.java
@RestController@RequestMapping("/api/orders")@RequiredArgsConstructorpublic class OrderController { private final DynamicEndpointHandler endpointHandler;
@PostMapping public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) { return endpointHandler.handle(request); }}
OrderRequest.java
@Data@Builderpublic class OrderRequest { @NotEmpty(message = "Order items are required") private List<OrderItem> items;
@Valid private ShippingAddress shippingAddress;
@NotNull(message = "Payment method is required") private PaymentMethod paymentMethod;}
@Datapublic class OrderItem { @NotNull(message = "Product ID is required") private Long productId;
@Min(value = 1, message = "Quantity must be at least 1") private int quantity;}
OrderProcessingStrategy.java
@Component("orderProcessingStrategy")@Transactionalpublic class OrderProcessingStrategy implements BusinessLogic<OrderRequest, OrderResponse> { private final ProductRepository productRepository; private final OrderRepository orderRepository; private final InventoryService inventoryService;
@Override public OrderResponse execute(OrderRequest request) { // Validate inventory validateInventory(request.getItems());
// Calculate total BigDecimal total = calculateTotal(request.getItems());
// Process payment PaymentResult payment = processPayment(request.getPaymentMethod(), total);
// Create order Order order = createOrder(request, total, payment);
// Update inventory updateInventory(request.getItems());
return mapToResponse(order); }}
2. Real-time Analytics Processing
AnalyticsEvent.java
@Data@Builderpublic class AnalyticsEvent { @NotBlank(message = "Event type is required") private String eventType;
@NotNull(message = "Timestamp is required") private LocalDateTime timestamp;
@Valid private Map<String, Object> properties;
@NotNull(message = "User ID is required") private String userId;}
AnalyticsProcessor.java
@Component("analyticsProcessor")public class AnalyticsProcessor implements BusinessLogic<AnalyticsEvent, AnalyticsResult> { private final KafkaTemplate<String, AnalyticsEvent> kafkaTemplate; private final RedisTemplate<String, String> redisTemplate;
@Override public AnalyticsResult execute(AnalyticsEvent event) { // Process real-time metrics updateRealTimeMetrics(event);
// Send to Kafka for batch processing sendToKafka(event);
// Return immediate insights return generateQuickInsights(event); }}
Testing Guide
1. Unit Testing
Testing Transformers
@ExtendWith(MockitoExtension.class)class OrderRequestTransformerTest { @InjectMocks private OrderRequestTransformer transformer;
@Test void shouldTransformValidRequest() { // Given String jsonRequest = """ { "items": [ {"productId": 1, "quantity": 2} ], "shippingAddress": { "street": "123 Main St", "city": "Boston", "zipCode": "02108" }, "paymentMethod": "CREDIT_CARD" } """;
// When OrderRequest result = transformer.transform(jsonRequest);
// Then assertNotNull(result); assertEquals(1, result.getItems().size()); assertEquals(2, result.getItems().get(0).getQuantity()); }}
Testing Business Logic
@ExtendWith(MockitoExtension.class)class OrderProcessingStrategyTest { @Mock private ProductRepository productRepository;
@Mock private InventoryService inventoryService;
@InjectMocks private OrderProcessingStrategy strategy;
@Test void shouldProcessValidOrder() { // Given OrderRequest request = createSampleOrder(); when(productRepository.findById(anyLong())) .thenReturn(Optional.of(createSampleProduct()));
// When OrderResponse response = strategy.execute(request);
// Then assertNotNull(response); verify(inventoryService).updateInventory(any()); }}
2. Integration Testing
Configuration Testing
@SpringBootTestclass EndpointConfigurationTest { @Autowired private ConfigurationLoader configLoader;
@Test void shouldLoadValidConfiguration() { // Given String configPath = "test-config.json";
// When List<EndpointConfig> configs = configLoader.loadConfiguration(configPath);
// Then assertFalse(configs.isEmpty()); configs.forEach(this::validateEndpointConfig); }}
End-to-End Testing
@SpringBootTest@AutoConfigureMockMvcclass OrderEndpointTest { @Autowired private MockMvc mockMvc;
@Test void shouldCreateOrder() throws Exception { // Given String requestBody = createSampleOrderJson();
// When/Then mockMvc.perform(post("/api/orders") .contentType(MediaType.APPLICATION_JSON) .content(requestBody)) .andExpect(status().isOk()) .andExpect(jsonPath("$.orderId").exists()) .andExpect(jsonPath("$.status").value("CREATED")); }}
3. Performance Testing
Load Testing with JMeter
<?xml version="1.0" encoding="UTF-8"?><jmeterTestPlan version="1.2" properties="5.0"> <hashTree> <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="REST Framework Load Test"> <elementProp name="TestPlan.user_defined_variables" elementType="Arguments"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="TestPlan.user_define_classpath"></stringProp> <boolProp name="TestPlan.functional_mode">false</boolProp> <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> <boolProp name="TestPlan.tearDown_on_shutdown">false</boolProp> </TestPlan> <hashTree> <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Order Creation Test"> <elementProp name="ThreadGroup.main_controller" elementType="LoopController"> <boolProp name="LoopController.continue_forever">false</boolProp> <stringProp name="LoopController.loops">100</stringProp> </elementProp> <stringProp name="ThreadGroup.num_threads">50</stringProp> <stringProp name="ThreadGroup.ramp_time">10</stringProp> <longProp name="ThreadGroup.start_time">1373789594000</longProp> <longProp name="ThreadGroup.end_time">1373789594000</longProp> <boolProp name="ThreadGroup.scheduler">false</boolProp> <stringProp name="ThreadGroup.duration"></stringProp> <stringProp name="ThreadGroup.delay"></stringProp> </ThreadGroup> </hashTree> </hashTree></jmeterTestPlan>
Best Practices
- Always validate input at both the transformer and business logic levels
- Use proper exception handling with custom exceptions
- Implement proper logging at all levels
- Use proper transaction management for database operations
- Implement proper security measures
- Use caching where appropriate
- Follow SOLID principles
- Write comprehensive tests
- Document all public APIs
- Use proper version control and branching strategies