TechVolt Electronics Project Landing 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.
It presents the project in a cleaner, shareable format while the original screens remain available below.
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.
Frontend
JavaFX shell, header, footer, and route-swapped page content.
State
AppState stores the current user, route, and page-specific selections.
Services
Auth, shop, and admin logic enforce rules before any data changes happen.
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)
- Header is fixed in the top region and is always visible.
- Main page content is route-driven and replaced by renderer.
- Footer is always rendered as part of page flow at the bottom.
- Footer is not fixed to viewport bottom.
- 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:ScrollPanecontainingVBox pageFlowpageFlowchildren always in order: (1) active page node, (2) footer node
4.2 Route Loop Model (replaces console while-loop)
- User action creates
NavIntent. - Router evaluates user state and permissions.
- Router returns
RouteDecision. - Renderer swaps page content.
- 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 ID | Owner Screen | Auth Required |
|---|---|---|
AUTH_LOGIN | AuthScreen | Guest only |
AUTH_REGISTER | AuthScreen | Guest only |
GUEST_PRODUCTS | AuthScreen | Guest only |
CUSTOMER_PRODUCTS | CustomerScreen | Logged-in user |
CUSTOMER_PRODUCT_DETAILS | CustomerScreen | Logged-in user |
CUSTOMER_CART | CustomerScreen | Logged-in user |
CUSTOMER_CHECKOUT | CustomerScreen | Logged-in user |
ADMIN_PRODUCTS | AdminScreen | Admin only |
ADMIN_PRODUCT_EDITOR | AdminScreen | Admin only |
ADMIN_USERS | AdminScreen | Admin only |
ADMIN_REPORTS | AdminScreen | Admin 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
MainFx — com.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
AppState — app
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
NavIntent — app
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()
Router — app
State-aware and role-aware route decision engine.
RouteDecision dispatch(NavIntent intent)
RouteDecision evaluateCurrentState()
boolean canAccess(Route route)
ScreenContext — view.contracts
Shared dependency container for all screens and pages.
AppState appState
Router router
UiRenderer renderer
AuthService authService
ShopService shopService
AdminService adminService
ProductImageService productImageService
UiRenderer — view.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)
AppShell — view.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()
ProductImageService — service.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.
LoginPageRegisterPageGuestProductsPageCustomerProductsPageProductDetailsPageCartPageCheckoutPageAdminProductsPageAdminProductEditorPageAdminUsersPageAdminReportsPage8) Existing Classes to Modify
| Class | Change Required |
|---|---|
Product | Add imageName, imagePath fields, constructor overload, and accessors |
CsvPersistenceUtil | Update write logic for 10-column product rows |
CsvBootstrapInitializer | Parse 10-column rows; fallback for 8-column legacy rows (empty image fields) |
ProductRepository | Add image metadata mutation methods and persist CSV after mutation |
AdminService | Add setProductImage() and removeProductImage() methods |
AuthService / ShopService | Minor wiring updates; no major behavior change required |
ConsoleUI | Keep temporarily for fallback console mode during JavaFX migration |
AbstractScreen / AuthScreen / CustomerScreen / AdminScreen | Refactor 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
- Launch JavaFX app → shell renders with header and footer visible.
- Header remains visible during all route changes.
- Footer appears after content; scrolling reveals it on long pages.
- Guest cannot navigate to customer or admin routes.
- Customer cannot access admin routes.
- Admin routes open correctly after admin login.
- Product image upload writes file to
data/images/and updatesproducts.csv. - Product image remove updates CSV and deletes file (best effort).
- Old 8-column
products.csvrows 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.
- Create 3 different files containing entity data.
- Use 4 different try-catch patterns for different error types.
- Use one try-catch with multiple
catchblocks and afinallyblock.
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
Main.maincreates repositories.- Repository static init delegates to
CsvBootstrapInitializer.initializeIfNeeded(). - Initializer ensures directory/files/headers.
- Initializer safely resets in-memory arrays/counts.
- Initializer loads users/products/orders/order_items CSV files.
- Initializer recalculates next IDs from loaded data.
- 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)
- Service requests a mutation in repository.
- Repository mutates in-memory array/count.
- Repository immediately calls CSV store
writeAll(...). - Store writes temp file and atomically replaces real file.
- 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 toerrors.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 byorderIdand 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 onsave,update,delete.ProductRepository.java: call bootstrap; persist onsave,update,delete,updateStock.OrderRepository.java: persist bothorders.csvandorder_items.csvon 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
- Auto-create CSV files with headers in empty
data/. - Restore users/products/orders from existing CSV files.
- Persist product mutations immediately to
products.csv. - Persist user mutations immediately to
users.csv. - Persist checkout results immediately to
orders.csvandorder_items.csv. - Return graceful error from CSV report export on IO failure.
- Use at least 4 distinct try-catch error types.
- Implement at least one multi-catch + finally code path.
Suggested Practical Build Sequence
- Add helpers:
CsvPaths,CsvEscaper,CsvErrorLogger. - Add model constructor/factory support for persisted IDs.
- Add CSV stores for users/products/orders/items.
- Add bootstrap initializer and wire startup.
- Modify repositories to persist on mutation.
- Harden
CsvReportServicewith try/catch/finally. - 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.
Add concrete OOP constructs with minimal regression risk and preserve current shop behavior.
Replacing MockDatabase with CSV/file persistence in this milestone.
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 AbstractScreenConsoleReportService/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
| Class | New Fields | New/Updated Methods | Why |
|---|---|---|---|
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 Area | Behavior | Outcome |
|---|---|---|
| Pricing lifecycle | Default: LOCK_AT_ADD; optional: RECALCULATE_AT_CHECKOUT | Deterministic totals with configurable strategy |
| Report format routing | Normalizes key and resolves via format map | Clear runtime polymorphism |
| ID lookup | Repository operations resolve by id value, not array index | Reliable CRUD after deletions |
| Money precision | Checkout and totals use decimal-safe arithmetic | Accurate 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 classdefault(no modifier): Accessible within the same packageprotected: Accessible within same package AND subclassespublic: 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
- Model Layer — Pure data structures (POJOs) with no logic
- Repository Layer — Data access and storage management (in-memory arrays)
- Service Layer — Business logic and rules
- 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
| Layer | Fields | Constructor | Public Methods | Private Methods | Static |
|---|---|---|---|---|---|
| Model (entities) | private | public | Getters/Setters | None | None |
| MockDatabase | public static | private | None | None | ALL |
| Repository | None | public | All CRUD | None | Init blocks only |
| Service | private | public | Business logic | Helpers | None |
| View (ConsoleUI) | private | public | start() only | All UI methods | One constant |