📱 Electronics Shop

Complete Architecture Documentation with Access Modifiers

Java Project - 4-Layer Architecture with Maximum Encapsulation

TechVolt Electronics Project Landing Page

Default page

A clean front page for the project, with the old architecture pages still inside the same HTML.

This landing page introduces the team, QA, departments, architecture, and design patterns without removing the original multi-screen documentation. Use the buttons above to switch between the new presentation page and the original deep-dive pages.

Java 17 JavaFX CSV persistence Router-driven UI Layered backend
What this page does

It presents the project in a cleaner, shareable format while the original screens remain available below.

What stays the same

The deeper architecture, CSV steps, and OOP blueprint pages are still part of the same file and can be switched to instantly.

Who we are

The people behind the project, with roles and the right amount of team energy.

MAXMUDOV V

Team lead. Keeps the project moving and the big decisions from drifting into chaos.

JAXONGIROV SH

Another senior, but busy. The kind of teammate whose calendar is already doing overtime.

KHABIBULLAYEV K

Aspiring developer intern. Learns quickly and keeps the energy useful.

ABDULLAYEV M

I like anime, clean code, and making the UI less boring than it first looked.

MEMBER X

Who is that? The placeholder legend that somehow makes every roster more interesting.

QA team

We checked the project 😎

PROKOFYEV D

ISAMXOJAYEV A

Departments in the project

Every major area has a job, and each department owns a slice of the system.

View layer

Landing page, JavaFX shell, pages, reusable components, and presentation helpers.

App state and router

AppState, NavIntent, RouteDecision, and Router control what page appears next.

Services

AuthService, ShopService, AdminService, pricing, report, and media logic.

Repositories

UserRepository, ProductRepository, OrderRepository, CartRepository, and CSV persistence.

Model layer

User, Product, Order, OrderItem, Cart, and MockDatabase as the shared store.

Architecture in short

Frontend presents, backend decides, repositories persist, and the router keeps the flow clean.

1

Frontend

JavaFX shell, header, footer, and route-swapped page content.

2

State

AppState stores the current user, route, and page-specific selections.

3

Services

Auth, shop, and admin logic enforce rules before any data changes happen.

4

Persistence

Repositories update MockDatabase and write CSV files immediately.

Design patterns used

The same project uses a handful of patterns that keep it understandable and flexible.

Repository

Separates data access from business logic and UI code.

Strategy

Used in pricing and report behavior through interchangeable services.

Facade

Service classes simplify access to multiple repository operations.

Command-style intent

NavIntent packages navigation actions before the router processes them.

State-aware routing

Router decisions depend on the current AppState and user role.

Shared store

MockDatabase acts as the in-memory data source for the entire app.

v4: Step 5 — JavaFX SPA Router + Product Image Refactor

Executive Summary

We are moving from a console UI loop to a JavaFX desktop frontend with SPA-style behavior.

The new UI model: one window (Stage), one shell layout, fixed header at top, dynamic page content in center, footer always present at the bottom of page content (not sticky), route decision made by a custom router based on app state.

The old while loop behavior from console is preserved conceptually as: intent → router decision → renderer swaps page.

1) Current vs Target Architecture

Current (Console)

  • Console interaction in menu loops
  • Navigation through numeric options
  • Rendering is text output in terminal

Target (JavaFX)

  • JavaFX event-driven navigation
  • Navigation based on route/state, not scanner input loops
  • Rendering through JavaFX Nodes
  • Same service and repository business logic reused underneath

2) Core UI Rules (Non-Negotiable)

  1. Header is fixed in the top region and is always visible.
  2. Main page content is route-driven and replaced by renderer.
  3. Footer is always rendered as part of page flow at the bottom.
  4. Footer is not fixed to viewport bottom.
  5. On long pages, users scroll to reach footer.

Runtime Baseline

  • JDK 17+
  • Maven wrapper in repository root
  • JavaFX dependencies managed by Maven
  • IntelliJ can open this repository directly as a Maven project

3) Target File Structure

* modified existing file  |  ** new file

.
|-- README.md *
|-- pom.xml *  /  mvnw *  /  mvnw.cmd *
|-- data
|   |-- users.csv  /  orders.csv  /  order_items.csv
|   |-- products.csv *
|   `-- images **
|       `-- .gitkeep **
`-- src/com/university/shopping
    |-- Main.java
    |-- Launcher.java *
    |-- MainFx.java *
    |-- app **
    |   |-- AppState.java **
    |   |-- Route.java **
    |   |-- NavIntentType.java **
    |   |-- NavIntent.java **
    |   |-- RouteDecision.java **
    |   `-- Router.java **
    |-- dto **
    |   `-- ProductImageUpdateRequest.java **
    |-- model
    |   |-- MockDatabase.java *
    |   |-- Product.java *    (+ imageName, imagePath)
    |   `-- (User, Order, OrderItem, Cart - unchanged)
    |-- repository
    |   |-- CsvBootstrapInitializer.java *
    |   |-- CsvPersistenceUtil.java *
    |   |-- ProductRepository.java *
    |   `-- (UserRepository, OrderRepository, CartRepository, ErrorLogger)
    |-- service
    |   |-- AuthService.java *
    |   |-- ShopService.java *
    |   |-- AdminService.java *
    |   |-- media **
    |   |   `-- ProductImageService.java **
    |   |-- pricing/*
    |   `-- report/*
    `-- view
        |-- ConsoleUI.java *
        |-- contracts
        |   |-- MenuActions.java *
        |   |-- ScreenComponent.java **
        |   `-- ScreenContext.java **
        |-- renderer
        |   `-- UiRenderer.java **
        |-- layout
        |   `-- AppShell.java **
        |-- components
        |   |-- HeaderNavComponent.java **
        |   |-- FooterComponent.java **
        |   |-- NotificationBarComponent.java **
        |   `-- ProductCardComponent.java **
        |-- pages
        |   |-- LoginPage.java **
        |   |-- RegisterPage.java **
        |   |-- GuestProductsPage.java **
        |   |-- CustomerProductsPage.java **
        |   |-- ProductDetailsPage.java **
        |   |-- CartPage.java **
        |   |-- CheckoutPage.java **
        |   |-- AdminProductsPage.java **
        |   |-- AdminProductEditorPage.java **
        |   |-- AdminUsersPage.java **
        |   `-- AdminReportsPage.java **
        |-- screens
        |   |-- AbstractScreen.java *
        |   |-- AuthScreen.java *
        |   |-- CustomerScreen.java *
        |   `-- AdminScreen.java *
        `-- styles
            |-- app.css **
            `-- theme.css **

4) Router + Renderer Model

4.1 Layout Model

Root is BorderPane:

  • top: header nav component (fixed)
  • center: ScrollPane containing VBox pageFlow
  • pageFlow children always in order: (1) active page node, (2) footer node

4.2 Route Loop Model (replaces console while-loop)

  1. User action creates NavIntent.
  2. Router evaluates user state and permissions.
  3. Router returns RouteDecision.
  4. Renderer swaps page content.
  5. Optional lifecycle hooks run (onBeforeLeave, onAfterEnter).

4.3 Access Guards

  • Guest routes: only when not logged in.
  • Customer routes: require logged-in non-admin user.
  • Admin routes: require admin user.
  • Invalid request: redirected with user-visible reason.

5) Route Map

Route IDOwner ScreenAuth Required
AUTH_LOGINAuthScreenGuest only
AUTH_REGISTERAuthScreenGuest only
GUEST_PRODUCTSAuthScreenGuest only
CUSTOMER_PRODUCTSCustomerScreenLogged-in user
CUSTOMER_PRODUCT_DETAILSCustomerScreenLogged-in user
CUSTOMER_CARTCustomerScreenLogged-in user
CUSTOMER_CHECKOUTCustomerScreenLogged-in user
ADMIN_PRODUCTSAdminScreenAdmin only
ADMIN_PRODUCT_EDITORAdminScreenAdmin only
ADMIN_USERSAdminScreenAdmin only
ADMIN_REPORTSAdminScreenAdmin only

6) Product Image Refactor

6.1 New Product Model Fields

private String imageName;   // shown in UI
private String imagePath;   // relative path persisted in CSV

6.2 CSV Schema Change

Old header:

id,name,price,category,description,stockQuantity,isDiscounted,discountPercentage

New header:

id,name,price,category,description,stockQuantity,isDiscounted,discountPercentage,imageName,imagePath

Backward compatibility: If row has 8 columns, default image fields to empty strings.

6.3 File Storage Policy

  • Store image files under data/images/.
  • Persist only relative path, never machine-specific absolute path.
  • Remove file on delete/replace as best effort.
  • Allowed extensions: png, jpg, jpeg, webp.

7) New Class Reference

MainFxcom.university.shopping

JavaFX entry application. Builds dependency graph and root shell.

Fields:
  AuthService authService
  ShopService shopService
  AdminService adminService
  AppState appState
  Router router
  UiRenderer renderer

Methods:
  void start(Stage stage)         // initialize app, create scene, render initial route
  static void main(String[] args) // launch JavaFX app

AppStateapp

Single source of UI state.

Fields:
  User currentUser
  Route currentRoute
  Integer selectedProductId
  String flashMessage
  boolean loading

Key method:
  String consumeFlashMessage()  // returns flash message and clears it

NavIntentapp

Immutable navigation input to router. Factory methods:

static NavIntent appStart()
static NavIntent open(Route route)
static NavIntent openProduct(int productId)
static NavIntent loginSuccess()
static NavIntent registerSuccess()
static NavIntent checkoutSuccess()
static NavIntent logout()

Routerapp

State-aware and role-aware route decision engine.

RouteDecision dispatch(NavIntent intent)
RouteDecision evaluateCurrentState()
boolean canAccess(Route route)

ScreenContextview.contracts

Shared dependency container for all screens and pages.

AppState appState
Router router
UiRenderer renderer
AuthService authService
ShopService shopService
AdminService adminService
ProductImageService productImageService

UiRendererview.renderer

Route-to-screen resolution and center content swap.

void render(Route route, ScreenContext context)
void setNotification(String message, boolean error)
ScreenComponent resolveScreen(Route route)

AppShellview.layout

Owns stable regions and enforces header/footer layout behavior.

Fields:
  BorderPane root
  HeaderNavComponent header
  ScrollPane scrollRoot
  VBox pageFlow
  FooterComponent footer

Methods:
  Parent buildRoot(ScreenContext context)
  void setPageContent(Node pageNode)  // keeps order: [pageNode, footer]
  BorderPane getRoot()

ProductImageServiceservice.media

Validates, copies, and removes product image files.

String storeProductImage(ProductImageUpdateRequest request)
  // validate extension + copy file -> returns stored relative path

boolean removeProductImage(String relativePath)
  // delete file -> returns delete status

boolean isSupportedImage(String fileName)
  // checks extension validity (png, jpg, jpeg, webp)

View Components

HeaderNavComponent

Role-aware top navigation with user badge. Methods: render(context), refresh(context).

FooterComponent

Footer at bottom of page flow (scroll to reach). Method: render(context).

NotificationBarComponent

Success/error banner. Methods: showSuccess(), showError(), clear().

ProductCardComponent

Reusable catalog card with image thumbnail and fallback placeholder.

Page Classes — view.pages

All pages implement Node render(ScreenContext context). Each page owns its controls and event listeners, calls service methods, emits router intents on transitions, and shows notification messages.

LoginPage
RegisterPage
GuestProductsPage
CustomerProductsPage
ProductDetailsPage
CartPage
CheckoutPage
AdminProductsPage
AdminProductEditorPage
AdminUsersPage
AdminReportsPage

8) Existing Classes to Modify

ClassChange Required
ProductAdd imageName, imagePath fields, constructor overload, and accessors
CsvPersistenceUtilUpdate write logic for 10-column product rows
CsvBootstrapInitializerParse 10-column rows; fallback for 8-column legacy rows (empty image fields)
ProductRepositoryAdd image metadata mutation methods and persist CSV after mutation
AdminServiceAdd setProductImage() and removeProductImage() methods
AuthService / ShopServiceMinor wiring updates; no major behavior change required
ConsoleUIKeep temporarily for fallback console mode during JavaFX migration
AbstractScreen / AuthScreen / CustomerScreen / AdminScreenRefactor from console rendering to JavaFX ScreenComponent wrappers

9) Phased Implementation Plan

Phase 1 — Foundation

Create AppState, Route, NavIntent, Router, RouteDecision. Build AppShell and UiRenderer. Confirm header/footer layout behavior.

Phase 2 — Auth + Guest Catalog

Implement LoginPage, RegisterPage, GuestProductsPage. Add route guards and redirects.

Phase 3 — Customer Flow

Implement customer products, details, cart, and checkout pages. Wire into router.

Phase 4 — Admin Flow

Implement admin products table, product editor, users management, and reports pages.

Phase 5 — Product Image Refactor

Add model fields, update CSV schema, implement ProductImageService, wire admin upload/remove workflows.

Phase 6 — Stabilization

Regression tests, manual UX checks, route guard verification, CSV backward-compatibility checks.

10) High-Order Flowchart

App Launch
  → MainFx: build services, state, router, renderer
        → Router.evaluateCurrentState()
              → UiRenderer.render(route, context)
                    → AppShell: Header (fixed top) + ScrollPane (center)
                          → pageFlow VBox:
                                [1] active page Node
                                [2] footer Node

User action (click / submit)
  → Create NavIntent (factory method)
        → Router.dispatch(intent)
              → Allowed?  → RouteDecision(target)          → UiRenderer.render()
              → Denied?   → RouteDecision(fallback+reason) → UiRenderer.render()

Admin image upload
  → ProductImageService.storeProductImage(request)   [validate + copy]
        → AdminService.setProductImage(productId, path, name)
              → ProductRepository: update metadata + persist products.csv
                    → Renderer: refresh current admin page

11) Definition of Done + Test Checklist

Definition of Done

  • JavaFX app runs from repository as Maven project.
  • Router-driven navigation works for guest / customer / admin.
  • Header is fixed at top across all route changes.
  • Footer is always rendered at content bottom and scroll-reachable.
  • Product images can be uploaded/removed by admin and are persisted to CSV.
  • Existing core business behavior (auth, cart, checkout, reports) remains correct.

Manual Test Checklist

  1. Launch JavaFX app → shell renders with header and footer visible.
  2. Header remains visible during all route changes.
  3. Footer appears after content; scrolling reveals it on long pages.
  4. Guest cannot navigate to customer or admin routes.
  5. Customer cannot access admin routes.
  6. Admin routes open correctly after admin login.
  7. Product image upload writes file to data/images/ and updates products.csv.
  8. Product image remove updates CSV and deletes file (best effort).
  9. Old 8-column products.csv rows load without errors (image fields default to empty).

v3: Step 3 Implementation (CSV Persistence + Error Handling)

0) Goal and Requirement Mapping

This page is based on STEP3_CSV_PERSISTENCE_IMPLEMENTATION.md.

  1. Create 3 different files containing entity data.
  2. Use 4 different try-catch patterns for different error types.
  3. Use one try-catch with multiple catch blocks and a finally block.

Planned fulfillment: entity CSV files (products.csv, users.csv, orders.csv, optional order_items.csv), startup bootstrap from CSV to MockDatabase, and immediate repository-triggered persistence on every mutation.

1) New Files Tree

.
|-- STEP3_CSV_PERSISTENCE_IMPLEMENTATION.md **
|-- data **
|   |-- products.csv **
|   |-- users.csv **
|   |-- orders.csv **
|   |-- order_items.csv **
|   `-- errors.log **
`-- src
    `-- com/university/shopping
        |-- Main.java *
        |-- model
        |   |-- MockDatabase.java *
        |   |-- User.java *
        |   |-- Product.java *
        |   `-- Order.java *
        |-- repository
        |   |-- UserRepository.java *
        |   |-- ProductRepository.java *
        |   |-- OrderRepository.java *
        |   `-- CartRepository.java *
        |-- persistence **
        |   |-- CsvPaths.java **
        |   |-- CsvErrorLogger.java **
        |   |-- CsvEscaper.java **
        |   |-- CsvBootstrapInitializer.java **
        |   |-- UserCsvStore.java **
        |   |-- ProductCsvStore.java **
        |   |-- OrderCsvStore.java **
        |   `-- OrderItemCsvStore.java **
        `-- service/report
            `-- CsvReportService.java *

Notes: order_items.csv is recommended for normalized order-item persistence. errors.log stores non-fatal parse/IO issues.

2) New Workflow and Data Flow

2.1 Startup Flow

  1. Main.main creates repositories.
  2. Repository static init delegates to CsvBootstrapInitializer.initializeIfNeeded().
  3. Initializer ensures directory/files/headers.
  4. Initializer safely resets in-memory arrays/counts.
  5. Initializer loads users/products/orders/order_items CSV files.
  6. Initializer recalculates next IDs from loaded data.
  7. Application continues with normal service/UI startup.

2.2 Runtime Read Flow

Services call repositories, repositories read from MockDatabase arrays, and no per-request disk reads are done.

2.3 Runtime Write Flow (Triggered Persistence)

  1. Service requests a mutation in repository.
  2. Repository mutates in-memory array/count.
  3. Repository immediately calls CSV store writeAll(...).
  4. Store writes temp file and atomically replaces real file.
  5. On failure, repository returns failure and logs error.

2.4 Report Export Flow

AdminService.exportSystemReport("csv") delegates to CsvReportService.exportReport(), which returns SUCCESS:<path> or ERROR:<message>.

3) CSV Schemas

data/products.csv

id,name,price,category,description,stockQuantity,isDiscounted,discountPercentage

data/users.csv

id,username,password,isAdmin,createdDate

data/orders.csv

id,userId,orderDate,totalPrice,status

data/order_items.csv

orderId,productId,productName,quantity,priceAtPurchase

4) Error Handling Plan

Type Exception Where Behavior
Type A IOException CSV stores and CsvReportService Return false or ERROR:<message>, log details.
Type B NumberFormatException Numeric CSV fields Skip malformed row, log row/file, continue parsing.
Type C DateTimeParseException createdDate, orderDate Skip malformed row, log parse error, continue initialization.
Type D IllegalArgumentException Schema mismatch/invalid booleans/empty required fields Throw or map to parse error; log and continue per fail policy.

4.5 Required Multi-Catch + Finally Block

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader(path));
    // parse rows
} catch (NumberFormatException e) {
    // row numeric issues
} catch (DateTimeParseException e) {
    // date issues
} catch (IOException e) {
    // file IO issues
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException closeError) {
            // log close error
        }
    }
}

5) File-by-File Implementation Order and Specs

Persistence Helpers

  • CsvPaths.java: centralize paths for data files and error log.
  • CsvEscaper.java: CSV escaping and robust line splitting.
  • CsvErrorLogger.java: append structured non-fatal logs to errors.log.

Model Adjustments

  • User.java: add overload/factory for persisted ID restore with optional auto-register control.
  • Product.java: add overload/factory for explicit persisted ID.
  • Order.java: add overload/factory for explicit persisted ID.
  • MockDatabase.java: add deterministic reset and next-ID recalculation helper(s).

CSV Stores

  • UserCsvStore: ensureFileWithHeader, loadAll, writeAll.
  • ProductCsvStore: ensureFileWithHeader, loadAll, writeAll.
  • OrderCsvStore: ensureFileWithHeader, loadAllWithoutItems, writeAll.
  • OrderItemCsvStore: grouped loading by orderId and full write support.

Bootstrap + Repository Wiring

  • CsvBootstrapInitializer.java: one-time idempotent CSV bootstrap with file ensuring, loading, and counter finalization.
  • UserRepository.java: call bootstrap; persist on save, update, delete.
  • ProductRepository.java: call bootstrap; persist on save, update, delete, updateStock.
  • OrderRepository.java: persist both orders.csv and order_items.csv on save/update path.
  • CartRepository.java: keep in-memory for this step (cart CSV is non-goal now).

Service and Main Wiring

  • CsvReportService.java: robust file write with defensive try/catch/finally and logging.
  • Main.java: preferred explicit bootstrap call for transparency/testability.

6-10) Integrity, Compatibility, Acceptance, Build Order, Non-Goals

Data Integrity and Safety

  • Always write to temp file then replace target.
  • Never partially rewrite target in-place.
  • Keep headers in all CSV files.
  • Validate column count before parsing.

Minimal Acceptance Checklist

  1. Auto-create CSV files with headers in empty data/.
  2. Restore users/products/orders from existing CSV files.
  3. Persist product mutations immediately to products.csv.
  4. Persist user mutations immediately to users.csv.
  5. Persist checkout results immediately to orders.csv and order_items.csv.
  6. Return graceful error from CSV report export on IO failure.
  7. Use at least 4 distinct try-catch error types.
  8. Implement at least one multi-catch + finally code path.

Suggested Practical Build Sequence

  1. Add helpers: CsvPaths, CsvEscaper, CsvErrorLogger.
  2. Add model constructor/factory support for persisted IDs.
  3. Add CSV stores for users/products/orders/items.
  4. Add bootstrap initializer and wire startup.
  5. Modify repositories to persist on mutation.
  6. Harden CsvReportService with try/catch/finally.
  7. Run manual admin/customer/report scenarios.

Non-Goals for This Step

  • Full relational DB migration.
  • Full ACID transactions.
  • Concurrent multi-process file locking strategy.

This Step 3 plan keeps current architecture intact while adding durable CSV persistence with explicit error handling and low disruption risk.

OOP Extension Blueprint (v2)

Scope

This screen translates OOP_EXTENSION_IMPLEMENTATION_PLAN.md into implementation-ready architecture visuals and method-level behavior. It is designed for coding, QA execution, and design review.

Goal

Add concrete OOP constructs with minimal regression risk and preserve current shop behavior.

Out of Scope (Now)

Replacing MockDatabase with CSV/file persistence in this milestone.

Strategy

Introduce role-based screens, pricing strategy interface, and report hierarchy while keeping existing data layer intact.

Architecture Graph

ConsoleUI (Coordinator)
   |
   +--> MenuActions (polymorphic)
   |      +--> CustomerScreen extends AbstractScreen
   |      +--> AdminScreen extends AbstractScreen
   |
    +--> ShopService --uses--> DiscountPolicy (interface)
    |                        +--> StandardDiscountPolicy
    |                        +--> SeasonalDiscountPolicy
    |                        +--> PricingMode (LOCK_AT_ADD default)
    |
    +--> AdminService --uses--> reportServicesByFormat map
                                     +--> "console" -> ConsoleReportService
                                     +--> "csv" -> CsvReportService

All services continue using repositories and MockDatabase as before.

Class Tree (Exact Target)

src/com/university/shopping/
├── Main.java (MODIFY wiring)
├── view/
│   ├── ConsoleUI.java (MODIFY, delegate role menus)
│   ├── contracts/
│   │   └── MenuActions.java (NEW interface)
│   └── screens/
│       ├── AbstractScreen.java (NEW abstract)
│       ├── CustomerScreen.java (NEW)
│       └── AdminScreen.java (NEW)
├── service/
│   ├── AuthService.java (UNCHANGED)
│   ├── ShopService.java (MODIFY)
│   ├── AdminService.java (MODIFY)
│   ├── pricing/
│   │   ├── DiscountPolicy.java (NEW interface)
│   │   ├── PricingMode.java (NEW enum)
│   │   ├── StandardDiscountPolicy.java (NEW)
│   │   └── SeasonalDiscountPolicy.java (NEW)
│   └── report/
│       ├── AbstractReportService.java (NEW abstract)
│       ├── ConsoleReportService.java (NEW)
│       └── CsvReportService.java (NEW)
└── repository/
    └── (NO mandatory changes)

Requirement Matrix

Requirement Concrete Implementation Where Demonstrated
2 Inheritance Examples CustomerScreen/AdminScreen extends AbstractScreen
ConsoleReportService/CsvReportService extends AbstractReportService
UI role menus and report export flows
2 Abstract Classes AbstractScreen, AbstractReportService Shared methods + abstract contracts
2 Interfaces MenuActions, DiscountPolicy Role menu behavior + pricing strategy
3 Overriding Examples CustomerScreen.showMenu()
AdminScreen.showMenu()
CsvReportService.exportReport()
Menu rendering and report output
3 Polymorphism Examples MenuActions active = ...
DiscountPolicy policy = ...
AbstractReportService reportService = reportServicesByFormat.get(format)
Runtime role switching, policy switching, report destination switching

Package and Class Specifications

Package: view/contracts

MenuActions (interface)

Methods: void showMenu(), void handleOption(int option)

// Example polymorphic usage
MenuActions active = authService.isAdmin() ? adminScreen : customerScreen;
active.showMenu();
int choice = getIntInput();
active.handleOption(choice);

Package: view/screens

AbstractScreen (abstract)

Fields: scanner, authService, shopService, adminService

Implemented: printTitle, pause, readIntSafe, truncate

Abstract: showMenu, handleOption

CustomerScreen

Browse products, details, add/remove cart, checkout.

AdminScreen

Manage products/users and export reports.

Package: service/pricing

DiscountPolicy (interface)

DiscountPolicy policy = new SeasonalDiscountPolicy(5.0);
double finalPrice = policy.apply(product);

StandardDiscountPolicy

Mirrors current Product.getFinalPrice() behavior.

SeasonalDiscountPolicy

Adds extra campaign percentage on top of standard discount.
Example: base 1000, product 10%, seasonal 5% => 850

PricingMode (enum)

LOCK_AT_ADD (default) locks price snapshot in cart.
RECALCULATE_AT_CHECKOUT recalculates with active policy.

Package: service/report

AbstractReportService (abstract)

Implemented: buildTimestamp, formatCurrency, buildHeader
Abstract: exportReport()

ConsoleReportService

Computes totals and prints report to terminal.

CsvReportService

Computes same metrics and writes CSV to configured path.

Method Contracts (Deep Dive)

Modified Existing Classes

ClassNew FieldsNew/Updated MethodsWhy
ShopService DiscountPolicy discountPolicy, PricingMode pricingMode setDiscountPolicy, setPricingMode Pluggable pricing behavior
AdminService Map<String, AbstractReportService> reportServicesByFormat setReportService, exportSystemReport(format) Pluggable report destination
ConsoleUI MenuActions customerScreen/adminScreen role delegation in start() Role polymorphism

Workflows (Visual)

Workflow A: Role-Based Menu

ConsoleUI.start()
    -> MenuActions active = isAdmin ? adminScreen : customerScreen
    -> active.showMenu()
    -> choice = getIntInput()
    -> active.handleOption(choice)

Workflow B: Checkout with Pricing Policy

CustomerScreen.checkout()
  -> ShopService.checkout(userId)
  -> default: use locked OrderItem.priceAtPurchase
  -> optional mode: recalculate via discountPolicy.apply(product)

Workflow C: Admin Report Export

AdminScreen.exportReport()
    -> AdminService.exportSystemReport(format)
    -> reportService = reportServicesByFormat.get(format)
    -> reportService.exportReport()
    // ConsoleReportService (prints) or CsvReportService (writes CSV)

Create, Modify, Rewrite, Improve

Create

MenuActions, AbstractScreen, CustomerScreen, AdminScreen, DiscountPolicy, PricingMode, StandardDiscountPolicy, SeasonalDiscountPolicy, AbstractReportService, ConsoleReportService, CsvReportService

Modify

ConsoleUI, ShopService, AdminService, Main

Remove

None required. Keep current repository/model persistence untouched.

Rewrite (Targeted)

Only role-menu handling in ConsoleUI rewritten into delegating screens.

Engineering Decisions

Decision AreaBehaviorOutcome
Pricing lifecycleDefault: LOCK_AT_ADD; optional: RECALCULATE_AT_CHECKOUTDeterministic totals with configurable strategy
Report format routingNormalizes key and resolves via format mapClear runtime polymorphism
ID lookupRepository operations resolve by id value, not array indexReliable CRUD after deletions
Money precisionCheckout and totals use decimal-safe arithmeticAccurate order totals

QA Scenarios

Scenario 1: Menu polymorphism

login as admin   -> menu shows "Product Management", "User Management"
login as customer -> menu shows "Add Product to Cart", "Checkout"

Scenario 2: Pricing strategy

StandardDiscountPolicy:  base 1000, discount 10% -> charged: 900
SeasonalDiscountPolicy(5): same product         -> charged: 850

Scenario 3: Report destination

format = "console" -> report printed in terminal
format = "csv"    -> SUCCESS:reports/system.csv
format = "xml"    -> ERROR:Unsupported format

📐 Architecture Overview

Access Modifier Guidelines

  • private: Accessible only within the same class
  • default (no modifier): Accessible within the same package
  • protected: Accessible within same package AND subclasses
  • public: Accessible from anywhere

Our Security Policy

  • Fields: Always private (encapsulation)
  • Internal helper methods: private
  • Methods called by other classes: public
  • Model/Repository/Service classes: public (dependency injection)
  • Static usage: ONLY in MockDatabase (simulates shared database)

4-Layer Architecture

  1. Model Layer — Pure data structures (POJOs) with no logic
  2. Repository Layer — Data access and storage management (in-memory arrays)
  3. Service Layer — Business logic and rules
  4. View Layer — User interface and I/O (Console-based)
com.university.shopping/
├── Main.java
├── model/
│   ├── User.java  /  Product.java  /  Order.java
│   ├── OrderItem.java  /  Cart.java
│   └── MockDatabase.java
├── repository/
│   ├── UserRepository.java  /  ProductRepository.java
│   ├── OrderRepository.java  /  CartRepository.java
├── service/
│   ├── AuthService.java  /  ShopService.java  /  AdminService.java
└── view/
    └── ConsoleUI.java

📦 Layer 1: Model Package

User.java

private int userId;
private String username;
private String password;
private boolean isAdmin;
private String createdDate;

// Getters for all fields
// Setters: setUsername, setPassword, setIsAdmin (NO setUserId)

Product.java

private int productId;
private String name;
private double price;
private String category;
private String description;
private int stockQuantity;
private boolean isDiscounted;
private double discountPercentage;

public double getFinalPrice()  // price * (1 - discountPct/100) if discounted

Order.java / OrderItem.java / Cart.java

Order holds OrderItem[]. OrderItem is fully immutable (no setters). Cart manages a 50-slot OrderItem[] with addItem, removeItem, clear, getTotalPrice.

MockDatabase.java STATIC ONLY

public static User[] users = new User[100];
public static int userCount = 0;
public static Product[] products = new Product[200];
public static int productCount = 0;
public static Order[] orders = new Order[500];
public static int orderCount = 0;
// ... nextUserId, nextProductId, nextOrderId

private MockDatabase() {}  // No instantiation

📦 Layer 2: Repository Package

Only layer that directly accesses MockDatabase. Seeded via static initialization blocks.

UserRepository — key methods

User findByUsername(String username)
User findById(int userId)
boolean save(User user)
boolean update(User user)
boolean delete(int userId)
User[] getAllUsers()
int getNextUserId()

ProductRepository — key methods

Product findById(int productId)
Product[] findAll()
boolean save(Product product)
boolean update(Product product)
boolean delete(int productId)
boolean updateStock(int productId, int quantityChange)  // critical
Product[] searchByName(String keyword)
int getNextProductId()

Seeded with 15 products (phones, laptops, accessories, tablets).

📦 Layer 3: Service Package

AuthService

String login(String username, String password)
  // returns: "SUCCESS" | "WRONG_PASSWORD" | "USER_NOT_FOUND"
String register(String username, String password)
  // returns: "SUCCESS" | "USERNAME_EXISTS" | "INVALID_PASSWORD"
void logout()
User getCurrentUser()
boolean isLoggedIn()
boolean isAdmin()
private boolean validatePassword(String password)
  // 6+ chars, must have letter AND digit

ShopService — Checkout Algorithm

1. Check isLoggedIn() or return "NOT_LOGGED_IN"
2. Get cart; check itemCount > 0 or return "EMPTY_CART"
3. VALIDATION: for each item, check stock >= quantity
   -> any fail: return "STOCK_ERROR:<productName>"
4. DEDUCTION: productRepository.updateStock(id, -qty) for each
5. CREATE ORDER: generate id, timestamp, calculate total, save
6. CLEAR CART: cartRepository.clear(userId)
7. Return "SUCCESS"

AdminService

Every method checks authService.isAdmin() first, returns "NOT_ADMIN" if false.

Product methods: addNewProduct, addStockToExistingProduct, removeStock, deleteProduct, updateProductPrice, updateProductName, setProductDiscount.

User methods: addUser, updateUser, deleteUser (prevents self-deletion), getAllUsers.

📦 Layer 4: View Package

ConsoleUI.java

Only public method: start() — main application loop. All UI methods are private.

public void start()   // while(true) loop -> mainMenu()
private void mainMenu()
private void showProductList()  // paginated, Z/M navigation
private void showBasket()
private void handleCheckout()
private void adminMenu()
private void adminProductMenu()
private void adminUserMenu()
// ... etc

📍 Main Entry Point

public static void main(String[] args) {
    // 1. Initialize Repositories (triggers static seeding blocks)
    UserRepository userRepo = new UserRepository();
    ProductRepository productRepo = new ProductRepository();
    OrderRepository orderRepo = new OrderRepository();
    CartRepository cartRepo = new CartRepository();

    // 2. Initialize Services (dependency injection)
    AuthService authService = new AuthService(userRepo);
    ShopService shopService = new ShopService(productRepo, orderRepo, cartRepo, authService);
    AdminService adminService = new AdminService(productRepo, userRepo, orderRepo, authService);

    // 3. Start UI loop
    ConsoleUI ui = new ConsoleUI(authService, shopService, adminService);
    ui.start();
}

🔐 Access Modifier Summary

LayerFieldsConstructorPublic MethodsPrivate MethodsStatic
Model (entities)privatepublicGetters/SettersNoneNone
MockDatabasepublic staticprivateNoneNoneALL
RepositoryNonepublicAll CRUDNoneInit blocks only
ServiceprivatepublicBusiness logicHelpersNone
View (ConsoleUI)privatepublicstart() onlyAll UI methodsOne constant