Review Create Service

Building on our ReviewService interface from the previous lesson, we'll now implement the core logic for creating restaurant reviews, including handling photos and updating restaurant ratings.

Service Implementation

The ReviewServiceImpl class is responsible for handling the business logic of creating reviews while maintaining data consistency.

Let's now implement the createReview method on the ReviewServiceImpl class:

package com.devtiro.restaurant.services.impl; import com.devtiro.restaurant.domain.ReviewCreateUpdateRequest; import com.devtiro.restaurant.domain.entities.Photo; import com.devtiro.restaurant.domain.entities.Restaurant; import com.devtiro.restaurant.domain.entities.Review; import com.devtiro.restaurant.domain.entities.User; import com.devtiro.restaurant.exceptions.RestaurantNotFoundException; import com.devtiro.restaurant.exceptions.ReviewNotAllowedException; import com.devtiro.restaurant.repositories.RestaurantRepository; import com.devtiro.restaurant.services.ReviewService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class ReviewServiceImpl implements ReviewService { private final RestaurantRepository restaurantRepository; @Override public Review createReview(User author, String restaurantId, ReviewCreateUpdateRequest createReview) { Restaurant restaurant = getRestaurantOrThrow(restaurantId); // Check if user has already reviewed this restaurant boolean hasExistingReview = restaurant.getReviews().stream() .anyMatch(r -> r.getWrittenBy().getId().equals(author.getId())); if (hasExistingReview) { throw new ReviewNotAllowedException("User has already reviewed this restaurant"); } LocalDateTime now = LocalDateTime.now(); // Create photos List<Photo> photos = createReview.getPhotoIds().stream().map(url -> { Photo photo = new Photo(); photo.setUrl(url); photo.setUploadDate(now); return photo; }).collect(Collectors.toList()); // Create review Review review = Review.builder() .id(UUID.randomUUID().toString()) .content(createReview.getContent()) .rating(createReview.getRating()) .photos(photos) .datePosted(now) .lastEdited(now) .writtenBy(author) .build(); // Add review to restaurant restaurant.getReviews().add(review); // Update restaurant's average rating updateRestaurantAverageRating(restaurant); // Save restaurant with new review Restaurant updatedRestaurant = restaurantRepository.save(restaurant); // Return the newly created review return updatedRestaurant.getReviews().stream() .filter(r -> r.getDatePosted().equals(review.getDatePosted())) .findFirst() .orElseThrow(() -> new RuntimeException("Error retrieving created review")); } private Restaurant getRestaurantOrThrow(String restaurantId) { return restaurantRepository.findById(restaurantId) .orElseThrow(() -> new RestaurantNotFoundException("Restaurant not found with id: " + restaurantId)); } private void updateRestaurantAverageRating(Restaurant restaurant) { List<Review> reviews = restaurant.getReviews(); if (reviews.isEmpty()) { restaurant.setAverageRating(0.0f); } else { float averageRating = (float) reviews.stream() .mapToDouble(Review::getRating) .average() .orElse(0.0); restaurant.setAverageRating(averageRating); } } }

Summary

  • Implemented ReviewServiceImpl with duplicate review prevention
  • Created helper methods for restaurant lookup and rating calculations
  • Updated restaurant average ratings
© 2026 Devtiro Ltd. All rights reserved