Appearance
Validation Rules
This document provides a comprehensive overview of the validation mechanisms and business rules enforced within the ticketing application. It covers field-level constraints, service-level business logic, and the resulting error responses.
Field Validation
Field-level validation is primarily handled at the Data Transfer Object (DTO) layer to ensure that incoming data from API requests is syntactically correct and complete before it reaches the service layer. The TicketRequest DTO utilizes Jakarta Bean Validation annotations to define these constraints.
The primary DTO for creating and updating tickets is TicketRequest.java. It enforces the following rules:
| Field | Constraint(s) | Description |
|---|---|---|
title | @NotBlank | The field must not be null and must contain at least one non-whitespace character. |
description | @NotBlank | The field must not be null and must contain at least one non-whitespace character. |
status | @NotNull | The field must not be null. It must correspond to a valid TicketStatus enum value. |
priority | @NotNull | The field must not be null. It must correspond to a valid TicketPriority enum value. |
requesterEmail | @NotBlank, @Email | The field must not be blank and must be a well-formed email address. |
assignedTo | (None) | This field is optional and has no validation constraints at the DTO level. |
The @NotBlank and @NotNull constraints are also mirrored in the Ticket entity to enforce data integrity at the persistence layer, as seen in src/main/java/com/slalom/demo/ticketing/model/Ticket.java. However, the @Email constraint on requesterEmail is only enforced at the DTO layer, not at the entity level. Additionally, the description column has a length = 2000 constraint defined in the @Column annotation.
For more details on the structure of this DTO, refer to the Data Models documentation.
Implementation Example: TicketRequest.java
The following code snippet from src/main/java/com/slalom/demo/ticketing/dto/TicketRequest.java shows the direct implementation of these validation rules.
java
package com.slalom.demo.ticketing.dto;
import com.slalom.demo.ticketing.model.TicketPriority;
import com.slalom.demo.ticketing.model.TicketStatus;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TicketRequest {
@NotBlank(message = "Title is required")
private String title;
@NotBlank(message = "Description is required")
private String description;
@NotNull(message = "Status is required")
private TicketStatus status;
@NotNull(message = "Priority is required")
private TicketPriority priority;
@NotBlank(message = "Requester email is required")
@Email(message = "Invalid email format")
private String requesterEmail;
private String assignedTo;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Bean Validation
The application leverages the Jakarta Bean Validation API (part of the spring-boot-starter-validation dependency) for its validation framework. Spring Boot provides seamless integration, automatically triggering validation for controller method arguments annotated with @Valid.
When an API endpoint receives a TicketRequest object in the request body, Spring's web layer automatically invokes the validator. If any of the constraints defined in TicketRequest.java are violated, the request is immediately rejected before it ever reaches the TicketService.
Custom Validation Messages
As shown in the code snippet above, each validation annotation includes a message attribute (e.g., message = "Title is required"). These custom messages are used in the error response, providing clear, user-friendly feedback to the API consumer. This is preferable to the often-generic default messages provided by the validation framework.
Business Rule Validation
Beyond simple field constraints, the application enforces more complex business rules within the service layer (TicketService.java). These rules govern the application's state and behavior.
Resolved Timestamp Logic
A key business rule is the automatic setting of the resolvedAt timestamp. This logic is implemented in the updateTicket method of the TicketService.
- Rule: When a ticket's status is updated to
RESOLVEDorCLOSED, theresolvedAttimestamp should be set to the current time. - Condition: This action only occurs if
resolvedAtwas previouslynull. This prevents the timestamp from being overwritten if the ticket is modified after being resolved.
java
// Snippet from src/main/java/com/slalom/demo/ticketing/service/TicketService.java
// ... inside updateTicket method ...
// Set resolved timestamp if status changed to RESOLVED or CLOSED
if ((request.getStatus() == TicketStatus.RESOLVED || request.getStatus() == TicketStatus.CLOSED)
&& ticket.getResolvedAt() == null) {
ticket.setResolvedAt(LocalDateTime.now());
}
Ticket updatedTicket = ticketRepository.save(ticket);
// ...1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
Entity Existence Validation
Before performing update or delete operations, the service validates that the target ticket actually exists in the database. This prevents NullPointerExceptions and ensures data integrity. If a ticket is not found, a RuntimeException is thrown, which is then handled by the global exception handler.
java
// Snippet from src/main/java/com/slalom/demo/ticketing/service/TicketService.java
public TicketResponse updateTicket(Long id, TicketRequest request) {
// ...
Ticket ticket = ticketRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Ticket not found with id: " + id));
// ...
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Areas for Future Enhancement
The current implementation has opportunities for more sophisticated business rule validation:
- Status Transition Rules: There is currently no logic to prevent invalid status transitions (e.g., moving from
CLOSEDback toOPEN). A state machine or explicit validation checks could be added to theupdateTicketmethod to enforce a valid ticket lifecycle. - Assignment Validation: The
assignedTofield is a simpleString. Future enhancements could involve validating that the assigned user exists in a separate user management system or is a member of the appropriate team.
Developers working on this module should consider implementing these checks in the TicketService to improve application robustness. For guidance on how to test these rules, see the Testing documentation.
Validation Error Responses
When a validation rule is violated, the API responds with a standardized error format to ensure a consistent client experience.
- HTTP Status Code: For bean validation failures, the API returns a
400 Bad Requeststatus code. This indicates that the server could not process the request due to a client-side error (i.e., invalid data). - Field-Specific Errors: The response body contains a structured JSON object detailing which fields failed validation and why. This allows the client to display targeted error messages to the user.
- Multiple Errors: If multiple fields are invalid, the error response will include an entry for each violation, allowing the client to handle all errors in a single response-request cycle.
For a detailed specification of the error response schema and examples, please consult the Error Handling API Reference.