Skip to main content

Examples

Practical examples using Spring Boot Result Starter.

Basic CRUD Controller

@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserService userService;

@GetMapping("/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
Result<User> result = userService.findById(id);
return ResponseUtils.asResponse(result);
}

@PostMapping
public ResponseEntity<?> createUser(@RequestBody CreateUserRequest request) {
Result<User> result = userService.createUser(request);
return ResponseUtils.asResponse(result);
}
}

Service Layer

@Service
public class UserService {

private final UserRepository userRepository;

public Result<User> findById(Long id) {
return userRepository.findById(id)
.map(Result::success)
.orElse(Result.entityNotFoundError("User not found"));
}

@RollbackOnFailure
public Result<User> createUser(CreateUserRequest request) {
if (userRepository.existsByEmail(request.getEmail())) {
return Result.entityAlreadyExistsError("Email already exists");
}

User user = new User(request.getName(), request.getEmail());
return Result.success(userRepository.save(user));
}
}

Validation Chains

public Result<User> validateAndCreateUser(CreateUserRequest request) {
return Result.success(new User(request.getName(), request.getEmail()))
.validate(user -> user.getName() != null, "Name is required")
.validate(user -> user.getEmail() != null, "Email is required")
.validate(user -> user.getEmail().contains("@"), "Invalid email")
.map(userRepository::save);
}

Async Operations

@GetMapping("/users/{id}/async")
public CompletableFuture<ResponseEntity<?>> getUserAsync(@PathVariable Long id) {
return Result.async(() -> userService.findById(id))
.thenApply(ResponseUtils::asResponse);
}

Bulk Operations

@PostMapping("/users/bulk")
public ResponseEntity<?> createUsers(@RequestBody List<CreateUserRequest> requests) {
List<Result<User>> results = requests.stream()
.map(userService::createUser)
.toList();

Result<List<User>> bulkResult = Result.combine(results);
return ResponseUtils.asResponse(bulkResult);
}

Event Handling

@Service
public class UserService {

@PublishEvent(on = PublishEvent.EventType.SUCCESS, eventName = "user-created")
@RollbackOnFailure
public Result<User> createUser(CreateUserRequest request) {
User user = new User(request.getName(), request.getEmail());
return Result.success(userRepository.save(user));
}
}

@Component
public class UserEventListener {

@EventListener
public void handleUserCreated(ResultEvent<?> event) {
if ("user-created".equals(event.getEventName()) && event.isSuccess()) {
User user = (User) event.getResult().getData();
// Send welcome email, etc.
}
}
}

Error Handling Patterns

public Result<UserProfile> getUserProfile(Long userId) {
return userService.findById(userId)
.flatMap(user -> {
UserProfile profile = new UserProfile(user);

// Optional enrichment - don't fail if unavailable
preferencesService.getPreferences(userId)
.onSuccess(profile::setPreferences)
.onFailure(error -> log.warn("Could not load preferences"));

return Result.success(profile);
});
}

Testing

@Test
void findById_WhenUserExists_ReturnsSuccess() {
// Given
User user = new User("John", "john@example.com");
when(userRepository.findById(1L)).thenReturn(Optional.of(user));

// When
Result<User> result = userService.findById(1L);

// Then
assertThat(result.isSuccess()).isTrue();
assertThat(result.getData()).isEqualTo(user);
}

@Test
void findById_WhenUserNotExists_ReturnsNotFoundError() {
// Given
when(userRepository.findById(1L)).thenReturn(Optional.empty());

// When
Result<User> result = userService.findById(1L);

// Then
assertThat(result.isSuccess()).isFalse();
assertThat(result.getError()).isInstanceOf(EntityNotFoundError.class);
}