It is a good practice to encapsulate business logic inside Service methods so that controllers and repositories are loosely coupled. The following is a Service written to encapsulate the business logic for Movie:
@Service
class MovieService constructor(val movieRepository: MovieRepository) {
fun findAll() = this.movieRepository.findAll();
fun save(movie : Movie) : Mono<Movie> = this.movieRepository.save(movie);
fun findOne(id : Int) : Mono<Movie> = this.movieRepository.findById(id)
.switchIfEmpty(Mono.error(MovieNotFoundException.create(id)));
fun rate(id : Int, comment : String, rating : Int) : Mono<Movie> {
var movieMono: Mono<Movie> = this.findOne(id);
return
movieMono.switchIfEmpty(Mono.error
(MovieNotFoundException.create(id))).map({ movie ->
movie.ratings.add(MovieRating(comment, rating, Date()));
movie;
}).map({ movie ->
this.save(movie).subscribe();
movie;
});
}
}
in the preceding code, MovieService is annotated with the @Service stereotype annotation to mark it as a Spring Service. When a movie specified by an ID does not exist, MovieNotFoundException is thrown. The following is the implementation of this:
class MovieNotFoundException : Exception {
constructor(message : String) : super(message);
companion object {
fun create(id : Int) : MovieNotFoundException {
return MovieNotFoundException("Movie by id $id, not
found");
}
}
}
The MovieNotFoundException phrase extends the Exception class and has a companion object to create instances with an appropriate message.
Likewise, the following UserService is used for User:
@Service
class UserService(val userRepository: UserRepository) {
fun getByUsername(username : String) : Flux<User> {
return userRepository.findByUsername(username);
}
fun save(user : User) : Mono<User> {
return userRepository.save(user);
}
}
This Service class has functions to save and get the user using their username. Also, there is a Spring Security Reactive-specific service that is implemented to load users by username using UserService and return as a Spring Security user to successfully authenticate users. The following MovieeReactiveUserDetailsService implements the ReactiveUserDetailsService.findByUsername() function:
@Service
class MovieeReactiveUserDetailsService(val userService : UserService) : ReactiveUserDetailsService {
override fun findByUsername(username: String?): Mono<UserDetails> {
if (username != null) {
return userService.getByUsername(username).toMono().map({
user -> User.withUsername(user.username).password
(user.password).roles(user.role).build();
});
}
return Mono.empty();
}
}
The preceding service makes use of UserService to get the user using their username and convert them into a Spring Security user, before returning Mono<UserDetails>.