Additional Framework Implementation Examples
3. Notification Service
NotificationRequest.java
@Data@Builderpublic class NotificationRequest { @NotNull(message = "Recipient is required") private NotificationRecipient recipient;
@NotBlank(message = "Template ID is required") private String templateId;
@Valid private Map<String, Object> templateVariables;
@NotNull(message = "Channel is required") private NotificationChannel channel;
@Future(message = "Scheduled time must be in the future") private LocalDateTime scheduledTime;
private NotificationPriority priority = NotificationPriority.NORMAL;}
@Datapublic class NotificationRecipient { @NotBlank(message = "User ID is required") private String userId;
@Email(message = "Valid email is required") private String email;
@Pattern(regexp = "^\\+[1-9]\\d{1,14}$", message = "Phone number must be in E.164 format") private String phoneNumber;}
NotificationProcessor.java
@Component("notificationProcessor")@Slf4jpublic class NotificationProcessor implements BusinessLogic<NotificationRequest, NotificationResult> { private final TemplateEngine templateEngine; private final NotificationChannelFactory channelFactory; private final NotificationRepository repository;
@Override @Transactional public NotificationResult execute(NotificationRequest request) { // Process template String content = templateEngine.process( request.getTemplateId(), request.getTemplateVariables() );
// Create notification Notification notification = Notification.builder() .recipient(request.getRecipient()) .content(content) .channel(request.getChannel()) .scheduledTime(request.getScheduledTime()) .priority(request.getPriority()) .status(NotificationStatus.PENDING) .build();
// Save notification notification = repository.save(notification);
// If immediate delivery is required if (request.getScheduledTime() == null || request.getScheduledTime().isBefore(LocalDateTime.now())) { return sendNotification(notification); }
return NotificationResult.builder() .notificationId(notification.getId()) .status(NotificationStatus.SCHEDULED) .scheduledTime(request.getScheduledTime()) .build(); }
private NotificationResult sendNotification(Notification notification) { try { NotificationChannel channel = channelFactory.getChannel(notification.getChannel()); DeliveryResult result = channel.send(notification);
notification.setStatus(result.isSuccess() ? NotificationStatus.DELIVERED : NotificationStatus.FAILED); notification.setDeliveryAttempts(notification.getDeliveryAttempts() + 1); notification.setLastAttemptTime(LocalDateTime.now()); notification.setErrorMessage(result.getErrorMessage());
repository.save(notification);
return NotificationResult.builder() .notificationId(notification.getId()) .status(notification.getStatus()) .deliveryTime(notification.getLastAttemptTime()) .errorMessage(result.getErrorMessage()) .build(); } catch (Exception e) { log.error("Failed to send notification: {}", notification.getId(), e); throw new NotificationDeliveryException("Failed to send notification", e); } }}
4. Report Generation Service
ReportRequest.java
@Data@Builderpublic class ReportRequest { @NotBlank(message = "Report type is required") private String reportType;
@Valid private DateRange dateRange;
private List<String> metrics;
private List<String> dimensions;
@Valid private ReportFormat format;
private Map<String, Object> filters;
@Max(value = 100000, message = "Maximum rows exceeded") private Integer maxRows;}
@Datapublic class DateRange { @NotNull(message = "Start date is required") @Past(message = "Start date must be in the past") private LocalDate startDate;
@NotNull(message = "End date is required") @Past(message = "End date must be in the past") private LocalDate endDate;
@AssertTrue(message = "Date range cannot exceed 1 year") public boolean isValidDateRange() { return startDate != null && endDate != null && !startDate.plusYears(1).isBefore(endDate); }}
ReportGenerator.java
@Component("reportGenerator")public class ReportGenerator implements BusinessLogic<ReportRequest, ReportResult> { private final ReportQueryBuilder queryBuilder; private final DataSourceRouter dataSource; private final ReportFormatFactory formatFactory; private final ReportCache reportCache;
@Override public ReportResult execute(ReportRequest request) { // Check cache first String cacheKey = generateCacheKey(request); ReportResult cachedResult = reportCache.get(cacheKey); if (cachedResult != null) { return cachedResult; }
// Build query Query query = queryBuilder .setType(request.getReportType()) .setDateRange(request.getDateRange()) .setMetrics(request.getMetrics()) .setDimensions(request.getDimensions()) .setFilters(request.getFilters()) .setMaxRows(request.getMaxRows()) .build();
// Execute query DataSet dataSet = dataSource.executeQuery(query);
// Format results ReportFormatter formatter = formatFactory.getFormatter(request.getFormat()); byte[] formattedReport = formatter.format(dataSet);
// Create result ReportResult result = ReportResult.builder() .reportId(UUID.randomUUID().toString()) .format(request.getFormat()) .data(formattedReport) .totalRows(dataSet.getRowCount()) .generatedAt(LocalDateTime.now()) .build();
// Cache result reportCache.put(cacheKey, result);
return result; }
private String generateCacheKey(ReportRequest request) { return DigestUtils.md5Hex(JsonUtils.toJson(request)); }}
5. Document Processing Service
DocumentRequest.java
@Data@Builderpublic class DocumentRequest { @NotNull(message = "Document type is required") private DocumentType documentType;
@NotNull(message = "Input document is required") private MultipartFile inputDocument;
@Valid private List<ProcessingOperation> operations;
private ProcessingPriority priority;
@Size(max = 10, message = "Maximum 10 callbacks allowed") private List<String> callbackUrls;}
@Datapublic class ProcessingOperation { @NotNull(message = "Operation type is required") private OperationType type;
private Map<String, Object> parameters;
@AssertTrue(message = "Invalid operation parameters") public boolean isValidOperation() { return OperationValidator.validate(type, parameters); }}
DocumentProcessor.java
@Component("documentProcessor")@Slf4jpublic class DocumentProcessor implements BusinessLogic<DocumentRequest, DocumentResult> { private final StorageService storageService; private final ProcessingOperationFactory operationFactory; private final CallbackService callbackService;
@Override public DocumentResult execute(DocumentRequest request) { // Store original document String documentId = storageService.store(request.getInputDocument());
// Create processing job ProcessingJob job = ProcessingJob.builder() .documentId(documentId) .operations(request.getOperations()) .priority(request.getPriority()) .status(ProcessingStatus.PENDING) .callbackUrls(request.getCallbackUrls()) .build();
// Process document if (request.getPriority() == ProcessingPriority.HIGH) { return processDocumentSync(job); } else { return processDocumentAsync(job); } }
private DocumentResult processDocumentSync(ProcessingJob job) { try { Document document = storageService.retrieve(job.getDocumentId());
for (ProcessingOperation operation : job.getOperations()) { DocumentProcessor processor = operationFactory.getProcessor(operation.getType()); document = processor.process(document, operation.getParameters()); }
// Store processed document String processedDocumentId = storageService.store(document);
return DocumentResult.builder() .jobId(job.getId()) .status(ProcessingStatus.COMPLETED) .outputDocumentId(processedDocumentId) .build();
} catch (Exception e) { log.error("Document processing failed: {}", job.getId(), e); throw new DocumentProcessingException("Processing failed", e); } }
private DocumentResult processDocumentAsync(ProcessingJob job) { // Submit to processing queue processingQueue.submit(job);
return DocumentResult.builder() .jobId(job.getId()) .status(ProcessingStatus.PROCESSING) .build(); }}
These examples demonstrate:
- Complex validation rules
- Async vs sync processing
- Caching strategies
- Multiple processing steps
- Error handling
- Callback mechanisms
- File handling
- Queue-based processing
- Different output formats
- Service integration
These examples can be easily extended and customized to suit specific business requirements. The framework provides a solid foundation for building scalable and maintainable REST services.