Available Restaurants Dashboard Flow
This document details the architectural execution flow, caching strategies, and business logic surrounding the available-restaurants-dashboard API endpoint, based strictly on the current backend codebase implementation.
1. Controller Layer (RestaurantController)
Endpoint: GET /api/restaurants/available-restaurants-dashboard
The flow begins when the client requests dashboard data. The controller orchestrates the retrieval of up to four distinct categories of restaurants to populate the UI.
- Initial Fetch: It first retrieves a "normal" or default page of available restaurants (
sort = "default") based on user coordinates (lat,lng). - Conditional Fetching: If the total number of branches returned in the initial fetch exceeds 10, the controller initiates three additional queries to build specialized dashboard sections:
- Top Rated: Uses default criteria (
sort = null, falling back to default sorting behavior internally). - Fastest: Sorted by delivery time (
sort = "delivery_time"). - Trending: Sorted by trending status (
sort = "trending").
- Top Rated: Uses default criteria (
- Response Construction: It aggregates these four lists into a
RestaurantDashboardDTOwhich is returned as a JSON response.
2. Service Layer & Geospatial Filtering (RestaurantServiceImpl)
Method: findAvailableRestaurants(...)
For each of the dashboard category calls, the service performs the following geospatial validation and processing:
- Coordinate Resolution: It determines the exact latitude and longitude either from the authenticated user's saved
deliveryLocationor from the request parameters (for guest users). - Redis Spatial Search: To narrow down the search space rapidly, it queries Redis using
GeoOperations. It searches within an 8km radius (MAX_SYSTEM_DELIVERY_RADIUS_KM) around the user's location in therestaurant:locationsindex. - Branch ID Extraction: It extracts the IDs of branches (
nearbyBranchIds) that fall within this physical radius. If none are found, it immediately returns an empty page. - Cache Delegation: It delegates the actual database query to the
RestaurantQueryCacheService, passing the requested sorting method, pagination, precise coordinates, and the list of nearby branch IDs.
3. Caching & Database Querying (RestaurantQueryCacheService)
Method: getCachedDashboardPage(...)
This layer heavily utilizes Spring Cache (backed by Redis) to reduce load on the database.
Cache Configuration:
- Cache Name:
dashboardRestaurants - Cache Key:
"#sort + ':' + #pageable.pageNumber + ':' + #roundedLat + ',' + #roundedLng" - Condition: Caches only if data is returned (
unless = "#result.content.isEmpty()"). TheroundedLatandroundedLngensure users in roughly the same grid area share the same cache entry for restaurant discovery.
Execution Logic (On Cache Miss):
- Dynamic Querying: It executes a
switchstatement on thesortparameter (e.g., "rating", "prep_time", "delivery_time", "trending"). Each case calls a specific dynamic sorting method on theRestaurantRepository. - Branch Validation: Before mapping to DTOs, it filters out invalid branches (e.g., branches with no manager, inactive branches, closed branches, or branches outside the
nearbyBranchIdslist). - DTO Mapping & Serialization: It invokes the
RestaurantDtoMapperto construct the DTOs and wraps the final result in aSerializablePageto allow safe storage within Redis.
4. DTO Construction & Filtering (RestaurantDtoMapper)
Method: mapToSummaryDto(...)
During the mapping process, the system dynamically calculates the real-time delivery feasibility and costs for the user.
- Delivery Calculation: For each valid branch belonging to a restaurant, it calls
PricingService.calculateDeliveryInfo(branch, userLatitude, userLongitude). - Deliverability Filtering: Branches are rigorously filtered. Only those where
deliveryInfo.isDeliverable()returnstrueare retained. - Data Population: The resulting
BranchSummaryDtois populated with precise, calculated fields includingdeliveryPrice,deliveryTime,distanceInKm, and a dynamicisTrendingflag (set to true if daily orders exceed 10). - Rounding Review Counts: To provide a cleaner UI, review counts are rounded dynamically (exact for <10, nearest 10 for <100, nearest 100 for >=100).
5. Pricing and Delivery Logic (PricingService)
Method: calculateDeliveryInfo(...)
This service is the source of truth for routing, pricing, and timing calculations.
- Cache Key Generation: It generates a
userLocationKeyby reducing coordinate precision to roughly a 250m-400m block (precision = 250). This prevents excessive identical API calls for users standing near each other. - Distance Retrieval: It delegates the actual route calculation to
DistanceMatrixService. - Deliverability Rules: A branch is considered deliverable ONLY if all three conditions are met:
- The branch is currently open.
- A valid route exists (
distanceInMeters != -1). - The actual road distance is less than or equal to the branch's defined delivery radius (
branch.getDeliveryRadiusInKm()).
- Time & Price Computation: If deliverable:
- Time: Calculated by adding the branch's average prep time, the Google API travel time, and a 5-10 minute courier buffer, then rounding to the nearest 5 minutes (e.g., "25-30 min").
- Price: Calculated using tiered pricing based on the actual road distance (e.g., <=1.5km = 80, <=3.0km = 150, <=5.0km = 200).
6. External API Integration (DistanceMatrixService)
Method: getDistanceMatrix(...)
This service handles the actual communication with the Google Maps Distance Matrix API.
- Caching: To optimize costs and speed, this layer is also cached individually (
@Cacheable(value = "distanceMatrix")). The key explicitly binds a branch to a rounded user location block ("'branch:' + #restaurantBranchId + ':loc:' + #userLocationKey"). - API Call: It requests driving distances (
TravelMode.DRIVING) using the metric system. - Error Handling: If the API fails or returns invalid results, it logs the error and gracefully returns a fallback object with values set to
-1L, which is later interpreted by thePricingServiceas an invalid/undeliverable route.