# Reviews Feature - Architecture Diagram ## Clean Architecture Flow ``` ┌─────────────────────────────────────────────────────────────────┐ │ PRESENTATION LAYER │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ UI Components │ │ │ │ - ProductTabsSection (_ReviewsTab) │ │ │ │ - WriteReviewPage │ │ │ │ - _ReviewItem widget │ │ │ └───────────────┬───────────────────────────────────────────┘ │ │ │ watches providers │ │ ┌───────────────▼───────────────────────────────────────────┐ │ │ │ Riverpod Providers (reviews_provider.dart) │ │ │ │ - productReviewsProvider(itemId) │ │ │ │ - productAverageRatingProvider(itemId) │ │ │ │ - productReviewCountProvider(itemId) │ │ │ │ - submitReviewProvider │ │ │ │ - deleteReviewProvider │ │ │ └───────────────┬───────────────────────────────────────────┘ │ └──────────────────┼───────────────────────────────────────────────┘ │ calls use cases ┌──────────────────▼───────────────────────────────────────────────┐ │ DOMAIN LAYER │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ Use Cases │ │ │ │ - GetProductReviews │ │ │ │ - SubmitReview │ │ │ │ - DeleteReview │ │ │ └───────────────┬───────────────────────────────────────────┘ │ │ │ depends on │ │ ┌───────────────▼───────────────────────────────────────────┐ │ │ │ Repository Interface (ReviewsRepository) │ │ │ │ - getProductReviews() │ │ │ │ - submitReview() │ │ │ │ - deleteReview() │ │ │ └───────────────┬───────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────▼───────────────────────────────────────────┐ │ │ │ Entities │ │ │ │ - Review │ │ │ │ - id, itemId, rating, comment │ │ │ │ - reviewerName, reviewerEmail, reviewDate │ │ │ │ - starsRating (computed: rating * 5) │ │ │ └───────────────────────────────────────────────────────────┘ │ └──────────────────┬───────────────────────────────────────────────┘ │ implemented by ┌──────────────────▼───────────────────────────────────────────────┐ │ DATA LAYER │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ Repository Implementation │ │ │ │ ReviewsRepositoryImpl │ │ │ │ - delegates to remote data source │ │ │ │ - converts models to entities │ │ │ │ - sorts reviews by date │ │ │ └───────────────┬───────────────────────────────────────────┘ │ │ │ uses │ │ ┌───────────────▼───────────────────────────────────────────┐ │ │ │ Remote Data Source (ReviewsRemoteDataSourceImpl) │ │ │ │ - makes HTTP requests via DioClient │ │ │ │ - handles response parsing │ │ │ │ - error handling & transformation │ │ │ └───────────────┬───────────────────────────────────────────┘ │ │ │ returns │ │ ┌───────────────▼───────────────────────────────────────────┐ │ │ │ Models (ReviewModel) │ │ │ │ - fromJson() / toJson() │ │ │ │ - toEntity() / fromEntity() │ │ │ │ - handles API response format │ │ │ └───────────────┬───────────────────────────────────────────┘ │ └──────────────────┼───────────────────────────────────────────────┘ │ communicates with ┌──────────────────▼───────────────────────────────────────────────┐ │ EXTERNAL SERVICES │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ Frappe/ERPNext API │ │ │ │ - POST /api/method/...item_feedback.get_list │ │ │ │ - POST /api/method/...item_feedback.update │ │ │ │ - POST /api/method/...item_feedback.delete │ │ │ └───────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────┘ ``` --- ## Data Flow: Fetching Reviews ``` User opens product detail page │ ▼ ProductTabsSection renders │ ▼ _ReviewsTab watches productReviewsProvider(itemId) │ ▼ Provider executes GetProductReviews use case │ ▼ Use case calls repository.getProductReviews() │ ▼ Repository calls remoteDataSource.getProductReviews() │ ▼ Data source makes HTTP POST to API │ ▼ API returns JSON response │ ▼ Data source parses JSON to List │ ▼ Repository converts models to List entities │ ▼ Repository sorts reviews by date (newest first) │ ▼ Provider returns AsyncValue> │ ▼ _ReviewsTab renders reviews with .when() │ ▼ User sees review list ``` --- ## Data Flow: Submitting Review ``` User clicks "Write Review" button │ ▼ Navigate to WriteReviewPage │ ▼ User selects stars (1-5) and enters comment │ ▼ User clicks "Submit" button │ ▼ Page validates form: - Rating: 1-5 stars ✓ - Comment: 20-1000 chars ✓ │ ▼ Convert stars to API rating: apiRating = stars / 5.0 │ ▼ Call submitReviewProvider.call() │ ▼ Use case validates: - Rating: 0-1 ✓ - Comment: not empty, 20-1000 chars ✓ │ ▼ Use case calls repository.submitReview() │ ▼ Repository calls remoteDataSource.submitReview() │ ▼ Data source makes HTTP POST to API │ ▼ API processes request and returns success │ ▼ Data source returns (void) │ ▼ Use case returns (void) │ ▼ Page invalidates productReviewsProvider(itemId) │ ▼ Page shows success SnackBar │ ▼ Page navigates back to product detail │ ▼ ProductTabsSection refreshes (due to invalidate) │ ▼ User sees updated review list with new review ``` --- ## Rating Scale Conversion Flow ``` UI Layer (Stars: 1-5) │ │ User selects 4 stars │ ▼ Convert to API: 4 / 5.0 = 0.8 │ ▼ Domain Layer (Rating: 0-1) │ │ Use case validates: 0 ≤ 0.8 ≤ 1 ✓ │ ▼ Data Layer sends: { "rating": 0.8 } │ ▼ API stores: rating = 0.8 │ ▼ API returns: { "rating": 0.8 } │ ▼ Data Layer parses: ReviewModel(rating: 0.8) │ ▼ Domain Layer converts: Review(rating: 0.8) │ │ Entity computes: starsRating = (0.8 * 5).round() = 4 │ ▼ UI Layer displays: ⭐⭐⭐⭐☆ ``` --- ## Error Handling Flow ``` User action (fetch/submit/delete) │ ▼ Try block starts │ ▼ API call may throw exceptions: │ ├─► DioException (timeout, connection, etc.) │ │ │ ▼ │ Caught by _handleDioException() │ │ │ ▼ │ Converted to app exception: │ - TimeoutException │ - NoInternetException │ - UnauthorizedException │ - ServerException │ - etc. │ ├─► ParseException (JSON parsing error) │ │ │ ▼ │ Rethrown as-is │ └─► Unknown error │ ▼ UnknownException(originalError, stackTrace) │ ▼ Exception propagates to provider │ ▼ Provider returns AsyncValue.error(exception) │ ▼ UI handles with .when(error: ...) │ ▼ User sees error message ``` --- ## Provider Dependency Graph ``` dioClientProvider │ ▼ reviewsRemoteDataSourceProvider │ ▼ reviewsRepositoryProvider │ ┌────────────┼────────────┐ ▼ ▼ ▼ getProductReviews submitReview deleteReview Provider Provider Provider │ │ │ ▼ │ │ productReviewsProvider│ │ (family) │ │ │ │ │ ┌──────┴──────┐ │ │ ▼ ▼ ▼ ▼ productAverage productReview (used directly RatingProvider CountProvider in UI components) (family) (family) ``` --- ## Component Interaction Diagram ``` ┌─────────────────────────────────────────────────────────────┐ │ ProductDetailPage │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ ProductTabsSection │ │ │ │ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ │ │ │ │ Specifications│ │ Reviews │ │ (other tab) │ │ │ │ │ │ Tab │ │ Tab │ │ │ │ │ │ │ └──────────────┘ └──────┬───────┘ └─────────────┘ │ │ │ │ │ │ │ │ │ ┌─────────────────────────▼───────────────────────┐ │ │ │ │ │ _ReviewsTab │ │ │ │ │ │ │ │ │ │ │ │ ┌───────────────────────────────────────────┐ │ │ │ │ │ │ │ WriteReviewButton │ │ │ │ │ │ │ │ (navigates to WriteReviewPage) │ │ │ │ │ │ │ └───────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌───────────────────────────────────────────┐ │ │ │ │ │ │ │ Rating Overview │ │ │ │ │ │ │ │ - Average rating (4.8) │ │ │ │ │ │ │ │ - Star display (⭐⭐⭐⭐⭐) │ │ │ │ │ │ │ │ - Review count (125 đánh giá) │ │ │ │ │ │ │ └───────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌───────────────────────────────────────────┐ │ │ │ │ │ │ │ _ReviewItem (repeated) │ │ │ │ │ │ │ │ - Avatar │ │ │ │ │ │ │ │ - Reviewer name │ │ │ │ │ │ │ │ - Date (2 tuần trước) │ │ │ │ │ │ │ │ - Star rating (⭐⭐⭐⭐☆) │ │ │ │ │ │ │ │ - Comment text │ │ │ │ │ │ │ └───────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ clicks "Write Review" ▼ ┌─────────────────────────────────────────────────────────────┐ │ WriteReviewPage │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Product Info Card (read-only) │ │ │ │ - Product image │ │ │ │ - Product name │ │ │ │ - Product code │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ StarRatingSelector │ │ │ │ ☆☆☆☆☆ → ⭐⭐⭐⭐☆ (4 stars selected) │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Comment TextField │ │ │ │ [ ] │ │ │ │ [ Multi-line text input ] │ │ │ │ [ ] │ │ │ │ 50 / 1000 ký tự │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ ReviewGuidelinesCard │ │ │ │ - Be honest and fair │ │ │ │ - Focus on the product │ │ │ │ - etc. │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ [Submit Button] │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ clicks "Submit" ▼ Validates & submits review │ ▼ Shows success SnackBar │ ▼ Navigates back to ProductDetailPage │ ▼ Reviews refresh automatically ``` --- ## State Management Lifecycle ``` 1. Initial State (Loading) ├─► productReviewsProvider returns AsyncValue.loading() └─► UI shows CustomLoadingIndicator 2. Loading State → Data State ├─► API call succeeds ├─► Provider returns AsyncValue.data(List) └─► UI shows review list 3. Data State → Refresh State (after submit) ├─► User submits new review ├─► ref.invalidate(productReviewsProvider) ├─► Provider state reset to loading ├─► API call re-executes └─► UI updates with new data 4. Error State ├─► API call fails ├─► Provider returns AsyncValue.error(exception) └─► UI shows error message 5. Empty State (special case of Data State) ├─► API returns empty list ├─► Provider returns AsyncValue.data([]) └─► UI shows "No reviews yet" message ``` --- ## Caching Strategy ``` Provider State Cache (Riverpod) │ ├─► Auto-disposed when widget unmounted │ (productReviewsProvider uses AutoDispose) │ ├─► Cache invalidated on: │ - User submits review │ - User deletes review │ - Manual ref.invalidate() call │ └─► Cache refresh: - Pull-to-refresh gesture (future enhancement) - App resume from background (future enhancement) - Time-based expiry (future enhancement) HTTP Cache (Dio CacheInterceptor) │ ├─► Reviews NOT cached (POST requests) │ (only GET requests cached by default) │ └─► Future: Implement custom cache policy - Cache reviews for 5 minutes - Invalidate on write operations ``` --- ## Testing Strategy ``` Unit Tests ├─► Domain Layer │ ├─► Use cases │ │ ├─► GetProductReviews │ │ ├─► SubmitReview (validates rating & comment) │ │ └─► DeleteReview │ └─► Entities │ └─► Review (starsRating computation) │ ├─► Data Layer │ ├─► Models (fromJson, toJson, toEntity) │ ├─► Remote Data Source (API calls, error handling) │ └─► Repository (model-to-entity conversion, sorting) │ └─► Presentation Layer └─► Providers (state transformations) Widget Tests ├─► _ReviewsTab │ ├─► Loading state │ ├─► Empty state │ ├─► Data state │ └─► Error state │ ├─► _ReviewItem │ ├─► Displays correct data │ ├─► Date formatting │ └─► Star rendering │ └─► WriteReviewPage ├─► Form validation ├─► Submit button states └─► Error messages Integration Tests └─► End-to-end flow ├─► Fetch reviews ├─► Submit review ├─► Verify refresh └─► Error scenarios ``` This architecture follows: - ✅ Clean Architecture principles - ✅ SOLID principles - ✅ Dependency Inversion (interfaces in domain layer) - ✅ Single Responsibility (each class has one job) - ✅ Separation of Concerns (UI, business logic, data separate) - ✅ Testability (all layers mockable)