problemhardood

OOD - Amazon-like Online Shopping System

HardUpdated: Dec 31, 2025

Problem

Design a comprehensive object-oriented e-commerce platform similar to Amazon that handles product catalog management, shopping cart operations, order processing, payment handling, inventory tracking, seller management, product reviews, and customer notifications. The system must support multiple user roles (Guest, Member, Seller, Admin), complex pricing strategies including discounts and promotions, order state management, shipment tracking, and search functionality. Your design should demonstrate SOLID principles and utilize design patterns to ensure the system is scalable, maintainable, and extensible as new features are added.

Solution

1. Requirements Analysis

Functional Requirements:

  • Manage product catalog with categories, hierarchies, and attributes (name, price, description, images)
  • Support seller accounts that can create listings, manage inventory, and view sales analytics
  • Enable product search by name, category, price range, ratings, and filters
  • Allow guest browsing without authentication; require registration for purchases
  • Shopping cart management: add/remove items, update quantities, save for later
  • Checkout workflow: address selection, payment method, order review, order placement
  • Multiple pricing strategies: base pricing, percentage discounts, bulk discounts, promotional codes
  • Order lifecycle management with state transitions (Pending → Processing → Shipped → Delivered → Completed)
  • Payment processing with multiple methods (Credit Card, PayPal, Gift Card)
  • Inventory tracking with real-time stock updates and low-stock alerts
  • Shipment tracking with carrier integration and delivery updates
  • Product reviews and ratings system with verification of purchase
  • Notification system for order confirmations, shipment updates, delivery alerts
  • Admin operations: approve/reject listings, manage categories, moderate reviews, handle refunds

Non-Functional Requirements:

  • Scalability: Support millions of products and thousands of concurrent users
  • Performance: Product search should return results within 2 seconds; cart operations should be near-instantaneous
  • Extensibility: New payment methods, shipping carriers, and pricing strategies should be easy to add (Open/Closed Principle)
  • Reliability: Order placement must be idempotent; payment transactions must be ACID-compliant
  • Security: Secure payment information, encrypt sensitive data, prevent inventory overselling
  • Maintainability: Clean separation of concerns with focused classes

2. Use Case Diagram

Actors:

  • Guest: Anonymous users who can browse products, search catalog, view details, and add items to cart
  • Member: Registered users who can do everything guests can, plus place orders, write reviews, track orders, manage wishlists
  • Seller: Vendors who create product listings, manage inventory, view sales reports, respond to reviews
  • Admin: Platform administrators who manage categories, approve listings, moderate content, process refunds
  • Payment Gateway: External service that processes payments
  • Shipping Carrier: External service that handles shipment logistics and tracking

Primary Use Cases:

  • Browse & Search Products - Find products using search and filters
  • View Product Details - See descriptions, images, reviews, ratings
  • Manage Shopping Cart - Add/remove items, update quantities
  • Checkout & Place Order - Complete purchase with payment
  • Process Payment - Handle payment authorization and capture
  • Manage Listings - Sellers create and update product listings
  • Track Inventory - Monitor stock levels and handle low-stock scenarios
  • Write & Read Reviews - Customers review products, others read reviews
  • Track Orders & Shipments - Monitor order status and delivery progress
  • Apply Discounts & Promotions - Calculate prices with various discount strategies
  • Manage Product Catalog - Admins organize categories and approve listings
graph TD
    subgraph EcommerceSystem["🛒 E-Commerce Platform"]
        UC1[🔍 Browse & Search Products]
        UC2[📦 View Product Details]
        UC3[🛒 Manage Shopping Cart]
        UC4[💳 Checkout & Place Order]
        UC5[💰 Process Payment]
        UC6[📝 Manage Product Listings]
        UC7[📊 Track Inventory]
        UC8[⭐ Write & Read Reviews]
        UC9[🚚 Track Orders & Shipments]
        UC10[🎁 Apply Discounts & Promotions]
        UC11[🗂️ Manage Product Catalog]
        UC12[📧 Send Notifications]
    end

    Guest[👤 Guest]
    Member[👨‍💼 Member]
    Seller[🏪 Seller]
    Admin[👨‍💻 Admin]
    PaymentGateway[💳 Payment Gateway]
    ShippingCarrier[🚛 Shipping Carrier]

    Guest --> UC1
    Guest --> UC2
    Guest --> UC3

    Member --> UC1
    Member --> UC2
    Member --> UC3
    Member --> UC4
    Member --> UC8
    Member --> UC9

    Seller --> UC6
    Seller --> UC7

    Admin --> UC11
    Admin --> UC6

    UC4 -.includes.-> UC5
    UC4 -.includes.-> UC10

    PaymentGateway --> UC5
    ShippingCarrier --> UC9

    System[🖥️ System]
    System --> UC12

    style Guest fill:#E3F2FD,stroke:#1976D2,stroke-width:2px
    style Member fill:#C8E6C9,stroke:#388E3C,stroke-width:2px
    style Seller fill:#FFF3E0,stroke:#F57C00,stroke-width:2px
    style Admin fill:#F3E5F5,stroke:#7B1FA2,stroke-width:2px
    style PaymentGateway fill:#FFE0B2,stroke:#E64A19,stroke-width:2px
    style ShippingCarrier fill:#B2DFDB,stroke:#00796B,stroke-width:2px
    style System fill:#E8F5E9,stroke:#388E3C,stroke-width:2px

    style UC1 fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style UC2 fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style UC3 fill:#81D4FA,stroke:#0277BD,stroke-width:2px
    style UC4 fill:#FFE082,stroke:#F57F17,stroke-width:2px
    style UC5 fill:#FFCC80,stroke:#EF6C00,stroke-width:2px
    style UC6 fill:#CE93D8,stroke:#7B1FA2,stroke-width:2px
    style UC7 fill:#E1BEE7,stroke:#8E24AA,stroke-width:2px
    style UC8 fill:#FFAB91,stroke:#D84315,stroke-width:2px
    style UC9 fill:#A5D6A7,stroke:#388E3C,stroke-width:2px
    style UC10 fill:#FFF59D,stroke:#FBC02D,stroke-width:2px
    style UC11 fill:#BA68C8,stroke:#6A1B9A,stroke-width:2px
    style UC12 fill:#C8E6C9,stroke:#2E7D32,stroke-width:2px

3. Class Diagram

Core Classes and Their Responsibilities:

User Management:

  • User (abstract) - Base class for all user types
  • Guest - Anonymous browsing, limited to viewing and cart operations
  • Member - Registered user with purchase history, wishlists, saved addresses
  • Seller - Vendor account with listings, inventory, and sales analytics
  • Admin - Platform administrator with moderation and catalog management capabilities

Product Catalog:

  • Product - Product entity with name, description, images, base price, category
  • ProductCategory - Hierarchical category structure
  • ProductListing - Seller's listing with inventory, pricing, and approval status
  • ProductImage - Product image with URL and display order
  • ProductAttribute - Key-value attributes (color, size, brand, etc.)

Shopping & Orders:

  • ShoppingCart - Session-based cart for guests and members
  • CartItem - Line item in cart with product, quantity, price snapshot
  • Order - Completed purchase with items, totals, payment, shipment
  • OrderItem - Line item in order with product snapshot and price
  • OrderStatus - State pattern for order lifecycle

Pricing Strategy Pattern:

  • PricingStrategy (interface) - Calculate final price
  • BasePricing - Standard product price
  • PercentageDiscount - Percentage off (e.g., 20% off)
  • BulkDiscount - Quantity-based discounts (buy 3, get 10% off)
  • PromotionalCode - Coupon code discounts

Payment:

  • Payment - Payment record with amount, method, status
  • PaymentMethod (interface) - Strategy for different payment types
  • CreditCardPayment, PayPalPayment, GiftCardPayment - Concrete payment implementations

Inventory:

  • Inventory - Stock tracking with quantity, warehouse location
  • InventoryTransaction - Record of stock changes (added, sold, returned, damaged)

Shipping:

  • Shipment - Shipping information with carrier, tracking number, status
  • ShippingAddress - Delivery address details
  • ShipmentEvent - Tracking events (picked up, in transit, delivered)

Reviews & Ratings:

  • Review - Customer review with rating, text, verified purchase flag
  • Rating - Numeric rating with aggregation methods

Notifications (Observer Pattern):

  • NotificationService - Manages notification delivery
  • EmailNotification, SMSNotification, PushNotification - Concrete notification types

Repositories (Repository Pattern):

  • ProductRepository - Product data access and search
  • OrderRepository - Order persistence and retrieval
  • UserRepository - User account management
classDiagram
    %% User Management
    class User {
        <<abstract>>
        -String userId
        -String email
        -String name
        -String phoneNumber
        -List~Address~ addresses
        +getProfile() UserProfile*
    }

    class Guest {
        -String sessionId
        -ShoppingCart cart
        +browseProducts() List~Product~
        +register() Member
    }

    class Member {
        -String passwordHash
        -List~Order~ orderHistory
        -Wishlist wishlist
        -List~PaymentMethod~ savedPaymentMethods
        +placeOrder(ShoppingCart) Order
        +writeReview(Product, Review) void
        +trackOrder(orderId) Order
    }

    class Seller {
        -String businessName
        -String taxId
        -List~ProductListing~ listings
        -SalesAnalytics analytics
        +createListing(Product) ProductListing
        +updateInventory(listing, quantity)
        +viewSalesReport() Report
    }

    class Admin {
        -List~String~ permissions
        +approveListing(ProductListing) void
        +rejectListing(ProductListing, reason) void
        +moderateReview(Review, action) void
        +manageCategories() void
    }

    User <|-- Guest
    User <|-- Member
    User <|-- Seller
    User <|-- Admin

    %% Product Catalog
    class Product {
        -String productId
        -String name
        -String description
        -Money basePrice
        -ProductCategory category
        -List~ProductImage~ images
        -List~ProductAttribute~ attributes
        -List~Review~ reviews
        +getAverageRating() double
        +getReviews() List~Review~
    }

    class ProductCategory {
        -String categoryId
        -String name
        -ProductCategory parent
        -List~ProductCategory~ children
        +isLeafCategory() bool
        +getPath() String
    }

    class ProductListing {
        -String listingId
        -Product product
        -Seller seller
        -Inventory inventory
        -ListingStatus status
        -LocalDateTime createdAt
        +submit() void
        +updatePrice(Money) void
        +isAvailable() bool
    }

    class Inventory {
        -String inventoryId
        -ProductListing listing
        -int quantity
        -int reservedQuantity
        -String warehouseLocation
        -List~InventoryTransaction~ transactions
        +reserve(quantity) bool
        +release(quantity) void
        +updateStock(quantity, reason) void
        +isInStock() bool
        +getLowStockThreshold() int
    }

    class ProductAttribute {
        -String key
        -String value
    }

    class ProductImage {
        -String imageUrl
        -int displayOrder
        -bool isPrimary
    }

    Product --> ProductCategory
    Product --> ProductImage
    Product --> ProductAttribute
    ProductListing --> Product
    ProductListing --> Seller
    ProductListing --> Inventory
    ProductCategory --> ProductCategory

    %% Shopping Cart
    class ShoppingCart {
        -String cartId
        -User user
        -List~CartItem~ items
        -LocalDateTime lastUpdated
        +addItem(ProductListing, quantity) void
        +removeItem(cartItemId) void
        +updateQuantity(cartItemId, quantity) void
        +calculateTotal(PricingStrategy) Money
        +clear() void
    }

    class CartItem {
        -String cartItemId
        -ProductListing listing
        -int quantity
        -Money priceSnapshot
        +getSubtotal() Money
    }

    ShoppingCart --> User
    ShoppingCart --> CartItem
    CartItem --> ProductListing

    %% Orders
    class Order {
        -String orderId
        -Member customer
        -List~OrderItem~ items
        -OrderStatus status
        -Money subtotal
        -Money tax
        -Money shippingCost
        -Money discount
        -Money total
        -Payment payment
        -ShippingAddress shippingAddress
        -Shipment shipment
        -LocalDateTime createdAt
        +calculateTotal(PricingStrategy) Money
        +updateStatus(OrderStatus) void
        +cancel() bool
        +canCancel() bool
    }

    class OrderItem {
        -String orderItemId
        -Product productSnapshot
        -int quantity
        -Money priceAtPurchase
        +getSubtotal() Money
    }

    class OrderStatus {
        <<enumeration>>
        PENDING
        PAYMENT_AUTHORIZED
        PROCESSING
        SHIPPED
        DELIVERED
        COMPLETED
        CANCELLED
        REFUNDED
    }

    Order --> Member
    Order --> OrderItem
    Order --> OrderStatus
    Order --> Payment
    Order --> ShippingAddress
    Order --> Shipment
    OrderItem --> Product

    %% Pricing Strategy Pattern
    class PricingStrategy {
        <<interface>>
        +calculatePrice(Product, quantity) Money
        +applyDiscount(Money) Money
    }

    class BasePricing {
        +calculatePrice(Product, quantity) Money
        +applyDiscount(Money) Money
    }

    class PercentageDiscount {
        -double discountRate
        -PricingStrategy baseStrategy
        +calculatePrice(Product, quantity) Money
        +applyDiscount(Money) Money
    }

    class BulkDiscount {
        -Map~Integer Money~ tierPricing
        -PricingStrategy baseStrategy
        +calculatePrice(Product, quantity) Money
    }

    class PromotionalCode {
        -String code
        -Money fixedDiscount
        -double percentageDiscount
        -LocalDateTime expiryDate
        -PricingStrategy baseStrategy
        +isValid() bool
        +applyDiscount(Money) Money
    }

    PricingStrategy <|.. BasePricing
    PricingStrategy <|.. PercentageDiscount
    PricingStrategy <|.. BulkDiscount
    PricingStrategy <|.. PromotionalCode
    PercentageDiscount --> PricingStrategy
    BulkDiscount --> PricingStrategy
    PromotionalCode --> PricingStrategy

    %% Payment
    class Payment {
        -String paymentId
        -Money amount
        -PaymentStatus status
        -PaymentMethod method
        -String transactionId
        -LocalDateTime timestamp
        +authorize() bool
        +capture() bool
        +refund(Money) bool
    }

    class PaymentMethod {
        <<interface>>
        +processPayment(Money) PaymentResult
        +refund(transactionId, Money) bool
    }

    class CreditCardPayment {
        -String cardNumber
        -String cvv
        -String expiryDate
        -String cardHolderName
        +processPayment(Money) PaymentResult
        +refund(transactionId, Money) bool
    }

    class PayPalPayment {
        -String paypalEmail
        -String authToken
        +processPayment(Money) PaymentResult
        +refund(transactionId, Money) bool
    }

    class GiftCardPayment {
        -String cardNumber
        -Money balance
        +processPayment(Money) PaymentResult
        +checkBalance() Money
    }

    Payment --> PaymentMethod
    PaymentMethod <|.. CreditCardPayment
    PaymentMethod <|.. PayPalPayment
    PaymentMethod <|.. GiftCardPayment

    %% Shipping
    class Shipment {
        -String shipmentId
        -Order order
        -String carrier
        -String trackingNumber
        -ShippingAddress destination
        -ShipmentStatus status
        -List~ShipmentEvent~ events
        -LocalDateTime estimatedDelivery
        +updateStatus(ShipmentStatus) void
        +addEvent(ShipmentEvent) void
        +getLatestEvent() ShipmentEvent
    }

    class ShipmentEvent {
        -LocalDateTime timestamp
        -String location
        -ShipmentStatus status
        -String description
    }

    class ShippingAddress {
        -String recipientName
        -String addressLine1
        -String addressLine2
        -String city
        -String state
        -String postalCode
        -String country
        -String phoneNumber
        +validate() bool
        +format() String
    }

    Shipment --> Order
    Shipment --> ShipmentEvent
    Shipment --> ShippingAddress

    %% Reviews
    class Review {
        -String reviewId
        -Member author
        -Product product
        -int rating
        -String title
        -String text
        -bool verifiedPurchase
        -LocalDateTime createdAt
        -int helpfulCount
        +markHelpful() void
        +isVerified() bool
    }

    class Rating {
        -Product product
        -double averageRating
        -int totalReviews
        -Map~Integer Integer~ ratingDistribution
        +calculateAverage() double
        +addRating(int) void
    }

    Review --> Member
    Review --> Product
    Rating --> Product

    %% Repositories
    class ProductRepository {
        <<interface>>
        +search(SearchCriteria) List~Product~
        +findById(productId) Product
        +save(Product) void
        +findByCategory(categoryId) List~Product~
    }

    class OrderRepository {
        <<interface>>
        +findById(orderId) Order
        +findByCustomer(customerId) List~Order~
        +save(Order) void
        +findByStatus(OrderStatus) List~Order~
    }

    class UserRepository {
        <<interface>>
        +findById(userId) User
        +findByEmail(email) User
        +save(User) void
        +authenticate(email, password) User
    }

Design Patterns Applied:

  1. Strategy Pattern: PricingStrategy enables flexible pricing with discounts, promotions, bulk pricing
  2. Strategy Pattern: PaymentMethod allows multiple payment implementations
  3. Repository Pattern: Abstracts data access for products, orders, users
  4. Observer Pattern: NotificationService notifies users of order/shipment updates
  5. State Pattern: OrderStatus manages order lifecycle transitions
  6. Factory Pattern: Creating different user types (Guest, Member, Seller, Admin)
  7. Decorator Pattern: (Implied) Wrapping pricing strategies for combined discounts

SOLID Principles Demonstrated:

  • Single Responsibility: Each class has one clear purpose (Product handles catalog, Order handles purchases)
  • Open/Closed: New payment methods and pricing strategies can be added without modifying existing code
  • Liskov Substitution: Any PaymentMethod or PricingStrategy can replace its parent
  • Interface Segregation: Focused repository interfaces instead of one large interface
  • Dependency Inversion: Services depend on repository interfaces, not concrete implementations

4. Activity Diagrams

Activity: Product Search and Browse

flowchart TD
    Start([👤 User visits platform]) --> HomePage[🏠 View homepage]
    HomePage --> SearchOrBrowse{🤔 Search or browse?}

    SearchOrBrowse -->|Search| EnterSearch[🔍 Enter search query]
    SearchOrBrowse -->|Browse| SelectCategory[📂 Select category]

    EnterSearch --> ApplyFilters[🎚️ Apply filters]
    SelectCategory --> ApplyFilters

    ApplyFilters --> ExecuteSearch[⚡ Execute search]
    ExecuteSearch --> Results{📊 Results found?}

    Results -->|No| NoResults[❌ Show no results]
    NoResults --> SuggestAlternatives[💡 Suggest alternatives]
    SuggestAlternatives --> End1([End])

    Results -->|Yes| DisplayProducts[📦 Display product grid]
    DisplayProducts --> UserAction{🤔 User action?}

    UserAction -->|Sort/Filter| ApplyFilters
    UserAction -->|View Product| ViewDetails[📄 View product details]
    UserAction -->|Exit| End1

    ViewDetails --> ShowImages[🖼️ Show images & description]
    ShowImages --> ShowReviews[⭐ Show reviews & ratings]
    ShowReviews --> ShowPrice[💰 Show price & stock]
    ShowPrice --> Decision{🤔 Add to cart?}

    Decision -->|No| UserAction
    Decision -->|Yes| AddCart[🛒 Add to cart]
    AddCart --> UpdateCart[✅ Update cart count]
    UpdateCart --> ContinueShopping{🛍️ Continue shopping?}

    ContinueShopping -->|Yes| UserAction
    ContinueShopping -->|No| End2([🛒 Proceed to cart])

    style Start fill:#E1F5FE,stroke:#01579B,stroke-width:3px
    style End1 fill:#FFCDD2,stroke:#C62828,stroke-width:3px
    style End2 fill:#C8E6C9,stroke:#2E7D32,stroke-width:3px

    style EnterSearch fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style SelectCategory fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style ApplyFilters fill:#81D4FA,stroke:#0277BD,stroke-width:2px
    style ExecuteSearch fill:#4FC3F7,stroke:#01579B,stroke-width:2px
    style DisplayProducts fill:#4FC3F7,stroke:#01579B,stroke-width:2px

    style ViewDetails fill:#CE93D8,stroke:#7B1FA2,stroke-width:2px
    style ShowImages fill:#E1BEE7,stroke:#8E24AA,stroke-width:2px
    style ShowReviews fill:#E1BEE7,stroke:#8E24AA,stroke-width:2px
    style ShowPrice fill:#E1BEE7,stroke:#8E24AA,stroke-width:2px

    style AddCart fill:#A5D6A7,stroke:#388E3C,stroke-width:2px
    style UpdateCart fill:#81C784,stroke:#2E7D32,stroke-width:2px

    style SearchOrBrowse fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style Results fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style UserAction fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style Decision fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style ContinueShopping fill:#FFF9C4,stroke:#F9A825,stroke-width:2px

    style NoResults fill:#FFCCBC,stroke:#E64A19,stroke-width:2px
    style SuggestAlternatives fill:#FFAB91,stroke:#D84315,stroke-width:2px

Activity: Checkout and Order Placement

flowchart TD
    Start([🛒 User views cart]) --> ReviewCart[📋 Review cart items]
    ReviewCart --> ModifyCart{✏️ Modify cart?}

    ModifyCart -->|Yes| UpdateItems[🔄 Update quantities/remove items]
    UpdateItems --> ReviewCart
    ModifyCart -->|No| CheckAuth{🔐 User authenticated?}

    CheckAuth -->|No| PromptLogin{💭 Login or continue?}
    PromptLogin -->|Login| Login[🔑 User logs in]
    PromptLogin -->|Guest Checkout| GuestInfo[📝 Enter guest information]
    Login --> ProceedCheckout
    GuestInfo --> ProceedCheckout

    CheckAuth -->|Yes| ProceedCheckout[➡️ Proceed to checkout]

    ProceedCheckout --> SelectAddress[🏠 Select shipping address]
    SelectAddress --> NewAddress{➕ New address?}
    NewAddress -->|Yes| EnterAddress[📝 Enter new address]
    NewAddress -->|No| UseExisting[✅ Use existing address]
    EnterAddress --> ValidateAddress
    UseExisting --> ValidateAddress

    ValidateAddress[✓ Validate address] --> SelectShipping[🚚 Select shipping method]
    SelectShipping --> CalculateShipping[💵 Calculate shipping cost]

    CalculateShipping --> EnterPayment[💳 Enter payment details]
    EnterPayment --> ApplyPromo{🎁 Apply promo code?}

    ApplyPromo -->|Yes| ValidatePromo[🔍 Validate promo code]
    ValidatePromo --> PromoValid{✅ Valid code?}
    PromoValid -->|No| PromoError[❌ Show error]
    PromoError --> ApplyPromo
    PromoValid -->|Yes| ApplyDiscount
    ApplyPromo -->|No| ApplyDiscount

    ApplyDiscount[💰 Apply discount] --> CalculateTotal[🧮 Calculate final total]
    CalculateTotal --> ReviewOrder[📄 Review order summary]
    ReviewOrder --> ConfirmOrder{✓ Confirm order?}

    ConfirmOrder -->|No| BackToCart([⬅️ Back to cart])
    ConfirmOrder -->|Yes| ReserveInventory[📦 Reserve inventory]

    ReserveInventory --> InventoryOK{✅ Stock available?}
    InventoryOK -->|No| OutOfStock[❌ Out of stock error]
    OutOfStock --> BackToCart

    InventoryOK -->|Yes| AuthorizePayment[💳 Authorize payment]
    AuthorizePayment --> PaymentOK{✅ Payment successful?}

    PaymentOK -->|No| PaymentFailed[❌ Payment declined]
    PaymentFailed --> ReleaseInventory[🔓 Release reserved inventory]
    ReleaseInventory --> RetryPayment{🔄 Retry payment?}
    RetryPayment -->|Yes| EnterPayment
    RetryPayment -->|No| BackToCart

    PaymentOK -->|Yes| CreateOrder[📝 Create order record]
    CreateOrder --> CapturePayment[💰 Capture payment]
    CapturePayment --> UpdateInventory[📉 Update inventory]
    UpdateInventory --> CreateShipment[🚛 Create shipment]
    CreateShipment --> SendConfirmation[📧 Send order confirmation]
    SendConfirmation --> DisplaySuccess[✅ Show order success page]
    DisplaySuccess --> End([🎉 Order placed successfully])

    style Start fill:#E1F5FE,stroke:#01579B,stroke-width:3px
    style End fill:#C8E6C9,stroke:#2E7D32,stroke-width:3px
    style BackToCart fill:#FFCDD2,stroke:#C62828,stroke-width:3px

    style ReviewCart fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style ProceedCheckout fill:#81D4FA,stroke:#0277BD,stroke-width:2px
    style SelectAddress fill:#4FC3F7,stroke:#01579B,stroke-width:2px
    style SelectShipping fill:#4FC3F7,stroke:#01579B,stroke-width:2px

    style Login fill:#CE93D8,stroke:#7B1FA2,stroke-width:2px
    style GuestInfo fill:#CE93D8,stroke:#7B1FA2,stroke-width:2px
    style EnterAddress fill:#E1BEE7,stroke:#8E24AA,stroke-width:2px

    style EnterPayment fill:#FFE082,stroke:#F57F17,stroke-width:2px
    style ValidatePromo fill:#FFE082,stroke:#F57F17,stroke-width:2px
    style AuthorizePayment fill:#FFCC80,stroke:#EF6C00,stroke-width:2px
    style CapturePayment fill:#FFAB91,stroke:#D84315,stroke-width:2px

    style ReserveInventory fill:#E1BEE7,stroke:#7B1FA2,stroke-width:2px
    style UpdateInventory fill:#CE93D8,stroke:#6A1B9A,stroke-width:2px

    style CreateOrder fill:#A5D6A7,stroke:#388E3C,stroke-width:2px
    style CreateShipment fill:#81C784,stroke:#2E7D32,stroke-width:2px
    style SendConfirmation fill:#66BB6A,stroke:#1B5E20,stroke-width:2px
    style DisplaySuccess fill:#4CAF50,stroke:#1B5E20,stroke-width:2px

    style CheckAuth fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style ModifyCart fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style PromptLogin fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style NewAddress fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style ApplyPromo fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style PromoValid fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style ConfirmOrder fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style InventoryOK fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style PaymentOK fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style RetryPayment fill:#FFF9C4,stroke:#F9A825,stroke-width:2px

    style OutOfStock fill:#FFCCBC,stroke:#E64A19,stroke-width:2px
    style PaymentFailed fill:#FFCCBC,stroke:#E64A19,stroke-width:2px
    style PromoError fill:#FFCCBC,stroke:#E64A19,stroke-width:2px

Activity: Order Fulfillment and Tracking

flowchart TD
    Start([📦 Order placed]) --> ProcessOrder[⚙️ System processes order]
    ProcessOrder --> AllocateWarehouse[🏭 Allocate to warehouse]
    AllocateWarehouse --> PickItems[📋 Pick items from inventory]

    PickItems --> ItemsAvailable{✅ All items available?}
    ItemsAvailable -->|No| PartialShip{🤔 Partial shipment?}
    PartialShip -->|Yes| SplitOrder[✂️ Split order]
    PartialShip -->|No| NotifyDelay[📧 Notify customer of delay]
    NotifyDelay --> WaitRestock[⏳ Wait for restock]
    WaitRestock --> PickItems

    SplitOrder --> PackShipment1
    ItemsAvailable -->|Yes| PackShipment1

    PackShipment1[📦 Pack items] --> QualityCheck[✓ Quality check]
    QualityCheck --> QC_Pass{✅ Pass QC?}

    QC_Pass -->|No| HandleDefect[🔧 Handle defective items]
    HandleDefect --> PickItems
    QC_Pass -->|Yes| GenerateLabel[🏷️ Generate shipping label]

    GenerateLabel --> AssignCarrier[🚛 Assign carrier]
    AssignCarrier --> CreateTracking[📍 Create tracking number]
    CreateTracking --> HandoffCarrier[📤 Handoff to carrier]
    HandoffCarrier --> UpdateStatus1[📊 Update status: SHIPPED]
    UpdateStatus1 --> NotifyShipped[📧 Notify customer: Shipped]

    NotifyShipped --> InTransit[🚚 In transit]
    InTransit --> TrackingUpdate{📡 Tracking event?}

    TrackingUpdate -->|Location Update| LogEvent1[📝 Log location event]
    LogEvent1 --> NotifyProgress[📧 Notify progress]
    NotifyProgress --> InTransit

    TrackingUpdate -->|Out for Delivery| OutForDelivery[🚙 Out for delivery]
    OutForDelivery --> NotifyDelivery[📧 Notify: Out for delivery]
    NotifyDelivery --> AttemptDelivery[🏠 Attempt delivery]

    AttemptDelivery --> DeliverySuccess{✅ Delivered?}

    DeliverySuccess -->|No| RetryDelivery{🔄 Retry?}
    RetryDelivery -->|Yes| RescheduleDelivery[📅 Reschedule delivery]
    RescheduleDelivery --> AttemptDelivery
    RetryDelivery -->|No| ReturnToSender[📮 Return to sender]
    ReturnToSender --> ProcessReturn[♻️ Process return]
    ProcessReturn --> RefundCustomer[💰 Refund customer]
    RefundCustomer --> End1([❌ Order returned])

    DeliverySuccess -->|Yes| ConfirmDelivery[✅ Confirm delivery]
    ConfirmDelivery --> UpdateStatus2[📊 Update status: DELIVERED]
    UpdateStatus2 --> NotifyDelivered[📧 Notify: Delivered]
    NotifyDelivered --> RequestReview[⭐ Request product review]
    RequestReview --> UpdateOrderStatus[📊 Update status: COMPLETED]
    UpdateOrderStatus --> ArchiveOrder[📁 Archive order]
    ArchiveOrder --> End2([🎉 Order completed])

    style Start fill:#E1F5FE,stroke:#01579B,stroke-width:3px
    style End1 fill:#FFCDD2,stroke:#C62828,stroke-width:3px
    style End2 fill:#C8E6C9,stroke:#2E7D32,stroke-width:3px

    style ProcessOrder fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style AllocateWarehouse fill:#81D4FA,stroke:#0277BD,stroke-width:2px
    style PickItems fill:#4FC3F7,stroke:#01579B,stroke-width:2px

    style PackShipment1 fill:#CE93D8,stroke:#7B1FA2,stroke-width:2px
    style QualityCheck fill:#E1BEE7,stroke:#8E24AA,stroke-width:2px
    style GenerateLabel fill:#BA68C8,stroke:#6A1B9A,stroke-width:2px

    style AssignCarrier fill:#FFCC80,stroke:#F57C00,stroke-width:2px
    style CreateTracking fill:#FFE0B2,stroke:#EF6C00,stroke-width:2px
    style HandoffCarrier fill:#FFAB91,stroke:#D84315,stroke-width:2px

    style InTransit fill:#B2DFDB,stroke:#00796B,stroke-width:2px
    style LogEvent1 fill:#80CBC4,stroke:#00695C,stroke-width:2px
    style OutForDelivery fill:#A5D6A7,stroke:#388E3C,stroke-width:2px

    style ConfirmDelivery fill:#81C784,stroke:#2E7D32,stroke-width:2px
    style UpdateStatus2 fill:#66BB6A,stroke:#1B5E20,stroke-width:2px
    style NotifyDelivered fill:#4CAF50,stroke:#1B5E20,stroke-width:2px
    style RequestReview fill:#8BC34A,stroke:#33691E,stroke-width:2px

    style NotifyShipped fill:#FFE082,stroke:#F57F17,stroke-width:2px
    style NotifyProgress fill:#FFF59D,stroke:#FBC02D,stroke-width:2px
    style NotifyDelivery fill:#FFF9C4,stroke:#F9A825,stroke-width:2px

    style ItemsAvailable fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style PartialShip fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style QC_Pass fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style TrackingUpdate fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style DeliverySuccess fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style RetryDelivery fill:#FFF9C4,stroke:#F9A825,stroke-width:2px

    style NotifyDelay fill:#FFCCBC,stroke:#E64A19,stroke-width:2px
    style HandleDefect fill:#FFCCBC,stroke:#E64A19,stroke-width:2px
    style ReturnToSender fill:#FFAB91,stroke:#D84315,stroke-width:2px

5. High-Level Code Implementation

Java

import java.time.*;
import java.util.*;
import java.math.BigDecimal;
import java.util.stream.Collectors;

// ============================================================================
// Enums
// ============================================================================

enum OrderStatus {
    PENDING,
    PAYMENT_AUTHORIZED,
    PROCESSING,
    SHIPPED,
    DELIVERED,
    COMPLETED,
    CANCELLED,
    REFUNDED
}

enum ListingStatus {
    DRAFT,
    SUBMITTED,
    APPROVED,
    REJECTED,
    INACTIVE
}

enum ShipmentStatus {
    PENDING,
    PICKED_UP,
    IN_TRANSIT,
    OUT_FOR_DELIVERY,
    DELIVERED,
    FAILED_DELIVERY,
    RETURNED
}

enum PaymentStatus {
    PENDING,
    AUTHORIZED,
    CAPTURED,
    FAILED,
    REFUNDED
}

enum AccountType {
    GUEST,
    MEMBER,
    SELLER,
    ADMIN
}

// ============================================================================
// Value Objects
// ============================================================================

class Money {
    private final BigDecimal amount;
    private final String currency;

    public Money(BigDecimal amount, String currency) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Amount cannot be negative");
        }
        this.amount = amount;
        this.currency = currency;
    }

    public Money add(Money other) {
        validateCurrency(other);
        return new Money(this.amount.add(other.amount), this.currency);
    }

    public Money subtract(Money other) {
        validateCurrency(other);
        return new Money(this.amount.subtract(other.amount), this.currency);
    }

    public Money multiply(double factor) {
        return new Money(this.amount.multiply(BigDecimal.valueOf(factor)), this.currency);
    }

    public Money multiply(int quantity) {
        return new Money(this.amount.multiply(BigDecimal.valueOf(quantity)), this.currency);
    }

    private void validateCurrency(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currency mismatch");
        }
    }

    public BigDecimal getAmount() { return amount; }
    public String getCurrency() { return currency; }

    @Override
    public String toString() {
        return currency + " " + amount.toString();
    }
}

class SearchCriteria {
    private String keyword;
    private String categoryId;
    private Money minPrice;
    private Money maxPrice;
    private Double minRating;
    private int page;
    private int pageSize;

    public SearchCriteria(String keyword) {
        this.keyword = keyword;
        this.page = 0;
        this.pageSize = 20;
    }

    // Getters and builder methods
    public SearchCriteria withCategory(String categoryId) {
        this.categoryId = categoryId;
        return this;
    }

    public SearchCriteria withPriceRange(Money min, Money max) {
        this.minPrice = min;
        this.maxPrice = max;
        return this;
    }

    public SearchCriteria withMinRating(double rating) {
        this.minRating = rating;
        return this;
    }

    public String getKeyword() { return keyword; }
    public String getCategoryId() { return categoryId; }
}

// ============================================================================
// User Management
// ============================================================================

abstract class User {
    protected String userId;
    protected String email;
    protected String name;
    protected String phoneNumber;
    protected List<ShippingAddress> addresses;
    protected AccountType accountType;

    public User(String userId, String email, String name, AccountType type) {
        this.userId = userId;
        this.email = email;
        this.name = name;
        this.accountType = type;
        this.addresses = new ArrayList<>();
    }

    public abstract boolean canPurchase();

    public void addAddress(ShippingAddress address) {
        addresses.add(address);
    }

    public String getUserId() { return userId; }
    public String getEmail() { return email; }
    public AccountType getAccountType() { return accountType; }
}

class Guest extends User {
    private String sessionId;
    private ShoppingCart cart;

    public Guest(String sessionId) {
        super("guest-" + sessionId, null, "Guest User", AccountType.GUEST);
        this.sessionId = sessionId;
        this.cart = new ShoppingCart(this);
    }

    @Override
    public boolean canPurchase() {
        return false; // Guests must register or checkout as guest
    }

    public Member register(String email, String password, String name) {
        return new Member(UUID.randomUUID().toString(), email, password, name);
    }

    public ShoppingCart getCart() { return cart; }
}

class Member extends User {
    private String passwordHash;
    private List<Order> orderHistory;
    private Wishlist wishlist;
    private List<PaymentMethod> savedPaymentMethods;
    private LocalDateTime registeredAt;

    public Member(String userId, String email, String passwordHash, String name) {
        super(userId, email, name, AccountType.MEMBER);
        this.passwordHash = passwordHash;
        this.orderHistory = new ArrayList<>();
        this.wishlist = new Wishlist(this);
        this.savedPaymentMethods = new ArrayList<>();
        this.registeredAt = LocalDateTime.now();
    }

    @Override
    public boolean canPurchase() {
        return true;
    }

    public Order placeOrder(ShoppingCart cart, ShippingAddress address, PaymentMethod payment) {
        Order order = new Order(this, cart, address);
        orderHistory.add(order);
        return order;
    }

    public Review writeReview(Product product, int rating, String title, String text) {
        boolean verifiedPurchase = orderHistory.stream()
            .flatMap(order -> order.getItems().stream())
            .anyMatch(item -> item.getProduct().getProductId().equals(product.getProductId()));

        return new Review(this, product, rating, title, text, verifiedPurchase);
    }

    public void addPaymentMethod(PaymentMethod method) {
        savedPaymentMethods.add(method);
    }

    public List<Order> getOrderHistory() { return orderHistory; }
    public Wishlist getWishlist() { return wishlist; }
}

class Seller extends User {
    private String businessName;
    private String taxId;
    private List<ProductListing> listings;
    private SalesAnalytics analytics;

    public Seller(String userId, String email, String businessName, String taxId) {
        super(userId, email, businessName, AccountType.SELLER);
        this.businessName = businessName;
        this.taxId = taxId;
        this.listings = new ArrayList<>();
        this.analytics = new SalesAnalytics(this);
    }

    @Override
    public boolean canPurchase() {
        return true; // Sellers can also purchase
    }

    public ProductListing createListing(Product product, int initialStock) {
        ProductListing listing = new ProductListing(this, product, initialStock);
        listings.add(listing);
        return listing;
    }

    public void updateInventory(ProductListing listing, int quantity, String reason) {
        listing.getInventory().updateStock(quantity, reason);
    }

    public List<ProductListing> getListings() { return listings; }
}

class Admin extends User {
    private List<String> permissions;

    public Admin(String userId, String email, String name) {
        super(userId, email, name, AccountType.ADMIN);
        this.permissions = new ArrayList<>();
    }

    @Override
    public boolean canPurchase() {
        return true;
    }

    public void approveListing(ProductListing listing) {
        listing.approve();
    }

    public void rejectListing(ProductListing listing, String reason) {
        listing.reject(reason);
    }

    public void moderateReview(Review review, String action) {
        // Implement review moderation logic
    }
}

class SalesAnalytics {
    private Seller seller;
    private Map<String, Integer> productSales;
    private Money totalRevenue;

    public SalesAnalytics(Seller seller) {
        this.seller = seller;
        this.productSales = new HashMap<>();
        this.totalRevenue = new Money(BigDecimal.ZERO, "USD");
    }

    public void recordSale(String productId, Money amount) {
        productSales.merge(productId, 1, Integer::sum);
        totalRevenue = totalRevenue.add(amount);
    }
}

class Wishlist {
    private String wishlistId;
    private Member member;
    private List<Product> items;

    public Wishlist(Member member) {
        this.wishlistId = UUID.randomUUID().toString();
        this.member = member;
        this.items = new ArrayList<>();
    }

    public void addItem(Product product) {
        if (!items.contains(product)) {
            items.add(product);
        }
    }

    public void removeItem(Product product) {
        items.remove(product);
    }

    public List<Product> getItems() { return items; }
}

// ============================================================================
// Product Catalog
// ============================================================================

class Product {
    private String productId;
    private String name;
    private String description;
    private Money basePrice;
    private ProductCategory category;
    private List<ProductImage> images;
    private List<ProductAttribute> attributes;
    private List<Review> reviews;
    private Rating rating;

    public Product(String productId, String name, String description, Money basePrice, ProductCategory category) {
        this.productId = productId;
        this.name = name;
        this.description = description;
        this.basePrice = basePrice;
        this.category = category;
        this.images = new ArrayList<>();
        this.attributes = new ArrayList<>();
        this.reviews = new ArrayList<>();
        this.rating = new Rating(this);
    }

    public void addImage(ProductImage image) {
        images.add(image);
    }

    public void addAttribute(String key, String value) {
        attributes.add(new ProductAttribute(key, value));
    }

    public void addReview(Review review) {
        reviews.add(review);
        rating.addRating(review.getRating());
    }

    public double getAverageRating() {
        return rating.getAverageRating();
    }

    public String getProductId() { return productId; }
    public String getName() { return name; }
    public Money getBasePrice() { return basePrice; }
    public ProductCategory getCategory() { return category; }
    public List<Review> getReviews() { return reviews; }
    public List<ProductImage> getImages() { return images; }
}

class ProductCategory {
    private String categoryId;
    private String name;
    private ProductCategory parent;
    private List<ProductCategory> children;

    public ProductCategory(String categoryId, String name, ProductCategory parent) {
        this.categoryId = categoryId;
        this.name = name;
        this.parent = parent;
        this.children = new ArrayList<>();

        if (parent != null) {
            parent.addChild(this);
        }
    }

    public void addChild(ProductCategory child) {
        children.add(child);
    }

    public boolean isLeafCategory() {
        return children.isEmpty();
    }

    public String getPath() {
        if (parent == null) {
            return name;
        }
        return parent.getPath() + " > " + name;
    }

    public String getCategoryId() { return categoryId; }
    public String getName() { return name; }
}

class ProductListing {
    private String listingId;
    private Product product;
    private Seller seller;
    private Inventory inventory;
    private ListingStatus status;
    private LocalDateTime createdAt;
    private String rejectionReason;

    public ProductListing(Seller seller, Product product, int initialStock) {
        this.listingId = UUID.randomUUID().toString();
        this.seller = seller;
        this.product = product;
        this.inventory = new Inventory(this, initialStock);
        this.status = ListingStatus.DRAFT;
        this.createdAt = LocalDateTime.now();
    }

    public void submit() {
        if (status == ListingStatus.DRAFT) {
            this.status = ListingStatus.SUBMITTED;
        }
    }

    public void approve() {
        if (status == ListingStatus.SUBMITTED) {
            this.status = ListingStatus.APPROVED;
        }
    }

    public void reject(String reason) {
        if (status == ListingStatus.SUBMITTED) {
            this.status = ListingStatus.REJECTED;
            this.rejectionReason = reason;
        }
    }

    public boolean isAvailable() {
        return status == ListingStatus.APPROVED && inventory.isInStock();
    }

    public String getListingId() { return listingId; }
    public Product getProduct() { return product; }
    public Seller getSeller() { return seller; }
    public Inventory getInventory() { return inventory; }
    public ListingStatus getStatus() { return status; }
}

class Inventory {
    private String inventoryId;
    private ProductListing listing;
    private int quantity;
    private int reservedQuantity;
    private String warehouseLocation;
    private List<InventoryTransaction> transactions;
    private static final int LOW_STOCK_THRESHOLD = 10;

    public Inventory(ProductListing listing, int initialQuantity) {
        this.inventoryId = UUID.randomUUID().toString();
        this.listing = listing;
        this.quantity = initialQuantity;
        this.reservedQuantity = 0;
        this.transactions = new ArrayList<>();
    }

    public synchronized boolean reserve(int requestedQuantity) {
        int available = quantity - reservedQuantity;
        if (available >= requestedQuantity) {
            reservedQuantity += requestedQuantity;
            transactions.add(new InventoryTransaction("RESERVED", requestedQuantity));
            return true;
        }
        return false;
    }

    public synchronized void release(int releaseQuantity) {
        reservedQuantity -= releaseQuantity;
        transactions.add(new InventoryTransaction("RELEASED", releaseQuantity));
    }

    public synchronized void updateStock(int delta, String reason) {
        quantity += delta;
        transactions.add(new InventoryTransaction(reason, delta));
    }

    public synchronized void confirmSale(int soldQuantity) {
        quantity -= soldQuantity;
        reservedQuantity -= soldQuantity;
        transactions.add(new InventoryTransaction("SOLD", -soldQuantity));
    }

    public boolean isInStock() {
        return (quantity - reservedQuantity) > 0;
    }

    public boolean isLowStock() {
        return (quantity - reservedQuantity) <= LOW_STOCK_THRESHOLD;
    }

    public int getAvailableQuantity() {
        return quantity - reservedQuantity;
    }

    public int getQuantity() { return quantity; }
}

class InventoryTransaction {
    private String transactionId;
    private String type;
    private int quantityChange;
    private LocalDateTime timestamp;

    public InventoryTransaction(String type, int quantityChange) {
        this.transactionId = UUID.randomUUID().toString();
        this.type = type;
        this.quantityChange = quantityChange;
        this.timestamp = LocalDateTime.now();
    }
}

class ProductAttribute {
    private String key;
    private String value;

    public ProductAttribute(String key, String value) {
        this.key = key;
        this.value = value;
    }

    public String getKey() { return key; }
    public String getValue() { return value; }
}

class ProductImage {
    private String imageUrl;
    private int displayOrder;
    private boolean isPrimary;

    public ProductImage(String imageUrl, int displayOrder, boolean isPrimary) {
        this.imageUrl = imageUrl;
        this.displayOrder = displayOrder;
        this.isPrimary = isPrimary;
    }

    public String getImageUrl() { return imageUrl; }
    public boolean isPrimary() { return isPrimary; }
}

// ============================================================================
// Shopping Cart
// ============================================================================

class ShoppingCart {
    private String cartId;
    private User user;
    private List<CartItem> items;
    private LocalDateTime lastUpdated;

    public ShoppingCart(User user) {
        this.cartId = UUID.randomUUID().toString();
        this.user = user;
        this.items = new ArrayList<>();
        this.lastUpdated = LocalDateTime.now();
    }

    public void addItem(ProductListing listing, int quantity) {
        // Check if item already exists in cart
        Optional<CartItem> existingItem = items.stream()
            .filter(item -> item.getListing().getListingId().equals(listing.getListingId()))
            .findFirst();

        if (existingItem.isPresent()) {
            existingItem.get().updateQuantity(existingItem.get().getQuantity() + quantity);
        } else {
            CartItem newItem = new CartItem(listing, quantity);
            items.add(newItem);
        }
        this.lastUpdated = LocalDateTime.now();
    }

    public void removeItem(String cartItemId) {
        items.removeIf(item -> item.getCartItemId().equals(cartItemId));
        this.lastUpdated = LocalDateTime.now();
    }

    public void updateQuantity(String cartItemId, int newQuantity) {
        items.stream()
            .filter(item -> item.getCartItemId().equals(cartItemId))
            .findFirst()
            .ifPresent(item -> item.updateQuantity(newQuantity));
        this.lastUpdated = LocalDateTime.now();
    }

    public Money calculateTotal(PricingStrategy pricingStrategy) {
        Money total = new Money(BigDecimal.ZERO, "USD");
        for (CartItem item : items) {
            Money itemPrice = pricingStrategy.calculatePrice(
                item.getListing().getProduct(),
                item.getQuantity()
            );
            total = total.add(itemPrice);
        }
        return total;
    }

    public void clear() {
        items.clear();
        this.lastUpdated = LocalDateTime.now();
    }

    public List<CartItem> getItems() { return items; }
    public String getCartId() { return cartId; }
}

class CartItem {
    private String cartItemId;
    private ProductListing listing;
    private int quantity;
    private Money priceSnapshot;

    public CartItem(ProductListing listing, int quantity) {
        this.cartItemId = UUID.randomUUID().toString();
        this.listing = listing;
        this.quantity = quantity;
        this.priceSnapshot = listing.getProduct().getBasePrice();
    }

    public void updateQuantity(int newQuantity) {
        if (newQuantity <= 0) {
            throw new IllegalArgumentException("Quantity must be positive");
        }
        this.quantity = newQuantity;
    }

    public Money getSubtotal() {
        return priceSnapshot.multiply(quantity);
    }

    public String getCartItemId() { return cartItemId; }
    public ProductListing getListing() { return listing; }
    public int getQuantity() { return quantity; }
}

// ============================================================================
// Orders
// ============================================================================

class Order {
    private String orderId;
    private Member customer;
    private List<OrderItem> items;
    private OrderStatus status;
    private Money subtotal;
    private Money tax;
    private Money shippingCost;
    private Money discount;
    private Money total;
    private Payment payment;
    private ShippingAddress shippingAddress;
    private Shipment shipment;
    private LocalDateTime createdAt;
    private List<OrderStatusHistory> statusHistory;

    public Order(Member customer, ShoppingCart cart, ShippingAddress address) {
        this.orderId = UUID.randomUUID().toString();
        this.customer = customer;
        this.items = new ArrayList<>();
        this.status = OrderStatus.PENDING;
        this.shippingAddress = address;
        this.createdAt = LocalDateTime.now();
        this.statusHistory = new ArrayList<>();

        // Convert cart items to order items
        for (CartItem cartItem : cart.getItems()) {
            OrderItem orderItem = new OrderItem(
                cartItem.getListing().getProduct(),
                cartItem.getQuantity(),
                cartItem.getSubtotal()
            );
            items.add(orderItem);
        }

        addStatusHistory(OrderStatus.PENDING);
    }

    public Money calculateTotal(PricingStrategy pricingStrategy) {
        this.subtotal = new Money(BigDecimal.ZERO, "USD");

        for (OrderItem item : items) {
            Money itemPrice = pricingStrategy.calculatePrice(item.getProduct(), item.getQuantity());
            subtotal = subtotal.add(itemPrice);
        }

        // Calculate tax (simplified - 10% tax rate)
        this.tax = subtotal.multiply(0.10);

        // Calculate shipping
        this.shippingCost = calculateShipping();

        // Apply discount if any
        if (this.discount == null) {
            this.discount = new Money(BigDecimal.ZERO, "USD");
        }

        // Total = subtotal + tax + shipping - discount
        this.total = subtotal.add(tax).add(shippingCost).subtract(discount);

        return total;
    }

    private Money calculateShipping() {
        // Simplified shipping calculation
        if (subtotal.getAmount().compareTo(new BigDecimal("50")) >= 0) {
            return new Money(BigDecimal.ZERO, "USD"); // Free shipping over $50
        }
        return new Money(new BigDecimal("5.99"), "USD");
    }

    public void updateStatus(OrderStatus newStatus) {
        this.status = newStatus;
        addStatusHistory(newStatus);
    }

    private void addStatusHistory(OrderStatus status) {
        statusHistory.add(new OrderStatusHistory(status, LocalDateTime.now()));
    }

    public boolean cancel() {
        if (canCancel()) {
            updateStatus(OrderStatus.CANCELLED);
            // Release inventory
            for (OrderItem item : items) {
                // Logic to release reserved inventory
            }
            return true;
        }
        return false;
    }

    public boolean canCancel() {
        return status == OrderStatus.PENDING || status == OrderStatus.PAYMENT_AUTHORIZED;
    }

    public void applyDiscount(Money discountAmount) {
        this.discount = discountAmount;
    }

    public String getOrderId() { return orderId; }
    public Member getCustomer() { return customer; }
    public List<OrderItem> getItems() { return items; }
    public OrderStatus getStatus() { return status; }
    public Money getTotal() { return total; }
    public void setPayment(Payment payment) { this.payment = payment; }
    public void setShipment(Shipment shipment) { this.shipment = shipment; }
}

class OrderItem {
    private String orderItemId;
    private Product productSnapshot;
    private int quantity;
    private Money priceAtPurchase;

    public OrderItem(Product product, int quantity, Money price) {
        this.orderItemId = UUID.randomUUID().toString();
        this.productSnapshot = product;
        this.quantity = quantity;
        this.priceAtPurchase = price;
    }

    public Money getSubtotal() {
        return priceAtPurchase;
    }

    public Product getProduct() { return productSnapshot; }
    public int getQuantity() { return quantity; }
}

class OrderStatusHistory {
    private OrderStatus status;
    private LocalDateTime timestamp;

    public OrderStatusHistory(OrderStatus status, LocalDateTime timestamp) {
        this.status = status;
        this.timestamp = timestamp;
    }
}

// ============================================================================
// Pricing Strategy Pattern
// ============================================================================

interface PricingStrategy {
    Money calculatePrice(Product product, int quantity);
    Money applyDiscount(Money originalPrice);
}

class BasePricing implements PricingStrategy {
    @Override
    public Money calculatePrice(Product product, int quantity) {
        return product.getBasePrice().multiply(quantity);
    }

    @Override
    public Money applyDiscount(Money originalPrice) {
        return originalPrice;
    }
}

class PercentageDiscount implements PricingStrategy {
    private double discountRate;
    private PricingStrategy baseStrategy;

    public PercentageDiscount(double discountRate, PricingStrategy baseStrategy) {
        this.discountRate = discountRate;
        this.baseStrategy = baseStrategy;
    }

    @Override
    public Money calculatePrice(Product product, int quantity) {
        Money basePrice = baseStrategy.calculatePrice(product, quantity);
        return applyDiscount(basePrice);
    }

    @Override
    public Money applyDiscount(Money originalPrice) {
        return originalPrice.multiply(1.0 - discountRate);
    }
}

class BulkDiscount implements PricingStrategy {
    private Map<Integer, Double> tierPricing; // quantity threshold -> discount rate
    private PricingStrategy baseStrategy;

    public BulkDiscount(PricingStrategy baseStrategy) {
        this.baseStrategy = baseStrategy;
        this.tierPricing = new HashMap<>();
        // Example: 3+ items = 5% off, 5+ items = 10% off, 10+ items = 15% off
        tierPricing.put(3, 0.05);
        tierPricing.put(5, 0.10);
        tierPricing.put(10, 0.15);
    }

    @Override
    public Money calculatePrice(Product product, int quantity) {
        Money basePrice = baseStrategy.calculatePrice(product, quantity);
        double discount = getDiscountForQuantity(quantity);
        return basePrice.multiply(1.0 - discount);
    }

    private double getDiscountForQuantity(int quantity) {
        return tierPricing.entrySet().stream()
            .filter(entry -> quantity >= entry.getKey())
            .map(Map.Entry::getValue)
            .max(Double::compareTo)
            .orElse(0.0);
    }

    @Override
    public Money applyDiscount(Money originalPrice) {
        return originalPrice;
    }
}

class PromotionalCode implements PricingStrategy {
    private String code;
    private Money fixedDiscount;
    private Double percentageDiscount;
    private LocalDateTime expiryDate;
    private PricingStrategy baseStrategy;

    public PromotionalCode(String code, Money fixedDiscount, LocalDateTime expiryDate, PricingStrategy baseStrategy) {
        this.code = code;
        this.fixedDiscount = fixedDiscount;
        this.expiryDate = expiryDate;
        this.baseStrategy = baseStrategy;
    }

    public PromotionalCode(String code, double percentageDiscount, LocalDateTime expiryDate, PricingStrategy baseStrategy) {
        this.code = code;
        this.percentageDiscount = percentageDiscount;
        this.expiryDate = expiryDate;
        this.baseStrategy = baseStrategy;
    }

    public boolean isValid() {
        return LocalDateTime.now().isBefore(expiryDate);
    }

    @Override
    public Money calculatePrice(Product product, int quantity) {
        if (!isValid()) {
            return baseStrategy.calculatePrice(product, quantity);
        }

        Money basePrice = baseStrategy.calculatePrice(product, quantity);
        return applyDiscount(basePrice);
    }

    @Override
    public Money applyDiscount(Money originalPrice) {
        if (!isValid()) {
            return originalPrice;
        }

        if (fixedDiscount != null) {
            Money discounted = originalPrice.subtract(fixedDiscount);
            return discounted.getAmount().compareTo(BigDecimal.ZERO) > 0 ?
                   discounted : new Money(BigDecimal.ZERO, originalPrice.getCurrency());
        } else if (percentageDiscount != null) {
            return originalPrice.multiply(1.0 - percentageDiscount);
        }
        return originalPrice;
    }

    public String getCode() { return code; }
}

// ============================================================================
// Payment
// ============================================================================

class Payment {
    private String paymentId;
    private Money amount;
    private PaymentStatus status;
    private PaymentMethod method;
    private String transactionId;
    private LocalDateTime timestamp;

    public Payment(Money amount, PaymentMethod method) {
        this.paymentId = UUID.randomUUID().toString();
        this.amount = amount;
        this.method = method;
        this.status = PaymentStatus.PENDING;
        this.timestamp = LocalDateTime.now();
    }

    public boolean authorize() {
        PaymentResult result = method.processPayment(amount);
        if (result.isSuccess()) {
            this.status = PaymentStatus.AUTHORIZED;
            this.transactionId = result.getTransactionId();
            return true;
        }
        this.status = PaymentStatus.FAILED;
        return false;
    }

    public boolean capture() {
        if (status == PaymentStatus.AUTHORIZED) {
            // In real implementation, this would call payment gateway
            this.status = PaymentStatus.CAPTURED;
            return true;
        }
        return false;
    }

    public boolean refund(Money refundAmount) {
        if (status == PaymentStatus.CAPTURED) {
            boolean success = method.refund(transactionId, refundAmount);
            if (success) {
                this.status = PaymentStatus.REFUNDED;
                return true;
            }
        }
        return false;
    }

    public PaymentStatus getStatus() { return status; }
}

class PaymentResult {
    private boolean success;
    private String transactionId;
    private String errorMessage;

    public PaymentResult(boolean success, String transactionId, String errorMessage) {
        this.success = success;
        this.transactionId = transactionId;
        this.errorMessage = errorMessage;
    }

    public boolean isSuccess() { return success; }
    public String getTransactionId() { return transactionId; }
    public String getErrorMessage() { return errorMessage; }
}

interface PaymentMethod {
    PaymentResult processPayment(Money amount);
    boolean refund(String transactionId, Money amount);
}

class CreditCardPayment implements PaymentMethod {
    private String cardNumber;
    private String cvv;
    private String expiryDate;
    private String cardHolderName;

    public CreditCardPayment(String cardNumber, String cvv, String expiryDate, String cardHolderName) {
        this.cardNumber = maskCardNumber(cardNumber);
        this.cvv = cvv;
        this.expiryDate = expiryDate;
        this.cardHolderName = cardHolderName;
    }

    private String maskCardNumber(String cardNumber) {
        if (cardNumber.length() < 4) return cardNumber;
        return "**** **** **** " + cardNumber.substring(cardNumber.length() - 4);
    }

    @Override
    public PaymentResult processPayment(Money amount) {
        // Simulate payment gateway call
        String transactionId = "TXN-" + UUID.randomUUID().toString();
        return new PaymentResult(true, transactionId, null);
    }

    @Override
    public boolean refund(String transactionId, Money amount) {
        // Simulate refund processing
        return true;
    }
}

class PayPalPayment implements PaymentMethod {
    private String paypalEmail;
    private String authToken;

    public PayPalPayment(String paypalEmail, String authToken) {
        this.paypalEmail = paypalEmail;
        this.authToken = authToken;
    }

    @Override
    public PaymentResult processPayment(Money amount) {
        // Simulate PayPal API call
        String transactionId = "PP-" + UUID.randomUUID().toString();
        return new PaymentResult(true, transactionId, null);
    }

    @Override
    public boolean refund(String transactionId, Money amount) {
        return true;
    }
}

class GiftCardPayment implements PaymentMethod {
    private String cardNumber;
    private Money balance;

    public GiftCardPayment(String cardNumber, Money balance) {
        this.cardNumber = cardNumber;
        this.balance = balance;
    }

    @Override
    public PaymentResult processPayment(Money amount) {
        if (balance.getAmount().compareTo(amount.getAmount()) >= 0) {
            balance = balance.subtract(amount);
            String transactionId = "GC-" + UUID.randomUUID().toString();
            return new PaymentResult(true, transactionId, null);
        }
        return new PaymentResult(false, null, "Insufficient gift card balance");
    }

    @Override
    public boolean refund(String transactionId, Money amount) {
        balance = balance.add(amount);
        return true;
    }

    public Money checkBalance() {
        return balance;
    }
}

// ============================================================================
// Shipping
// ============================================================================

class Shipment {
    private String shipmentId;
    private Order order;
    private String carrier;
    private String trackingNumber;
    private ShippingAddress destination;
    private ShipmentStatus status;
    private List<ShipmentEvent> events;
    private LocalDateTime estimatedDelivery;

    public Shipment(Order order, String carrier, ShippingAddress destination) {
        this.shipmentId = UUID.randomUUID().toString();
        this.order = order;
        this.carrier = carrier;
        this.trackingNumber = generateTrackingNumber();
        this.destination = destination;
        this.status = ShipmentStatus.PENDING;
        this.events = new ArrayList<>();
        this.estimatedDelivery = LocalDateTime.now().plusDays(5);
    }

    private String generateTrackingNumber() {
        return "TRACK-" + System.currentTimeMillis();
    }

    public void updateStatus(ShipmentStatus newStatus) {
        this.status = newStatus;
        addEvent(new ShipmentEvent(newStatus, "Status updated", "Warehouse"));
    }

    public void addEvent(ShipmentEvent event) {
        events.add(event);
    }

    public ShipmentEvent getLatestEvent() {
        return events.isEmpty() ? null : events.get(events.size() - 1);
    }

    public String getShipmentId() { return shipmentId; }
    public String getTrackingNumber() { return trackingNumber; }
    public ShipmentStatus getStatus() { return status; }
}

class ShipmentEvent {
    private LocalDateTime timestamp;
    private String location;
    private ShipmentStatus status;
    private String description;

    public ShipmentEvent(ShipmentStatus status, String description, String location) {
        this.timestamp = LocalDateTime.now();
        this.status = status;
        this.description = description;
        this.location = location;
    }

    public LocalDateTime getTimestamp() { return timestamp; }
    public String getDescription() { return description; }
}

class ShippingAddress {
    private String recipientName;
    private String addressLine1;
    private String addressLine2;
    private String city;
    private String state;
    private String postalCode;
    private String country;
    private String phoneNumber;

    public ShippingAddress(String recipientName, String addressLine1, String city,
                          String state, String postalCode, String country) {
        this.recipientName = recipientName;
        this.addressLine1 = addressLine1;
        this.city = city;
        this.state = state;
        this.postalCode = postalCode;
        this.country = country;
    }

    public boolean validate() {
        return recipientName != null && !recipientName.isEmpty() &&
               addressLine1 != null && !addressLine1.isEmpty() &&
               city != null && postalCode != null && country != null;
    }

    public String format() {
        StringBuilder sb = new StringBuilder();
        sb.append(recipientName).append("\n");
        sb.append(addressLine1).append("\n");
        if (addressLine2 != null && !addressLine2.isEmpty()) {
            sb.append(addressLine2).append("\n");
        }
        sb.append(city).append(", ").append(state).append(" ").append(postalCode).append("\n");
        sb.append(country);
        return sb.toString();
    }
}

// ============================================================================
// Reviews & Ratings
// ============================================================================

class Review {
    private String reviewId;
    private Member author;
    private Product product;
    private int rating;
    private String title;
    private String text;
    private boolean verifiedPurchase;
    private LocalDateTime createdAt;
    private int helpfulCount;

    public Review(Member author, Product product, int rating, String title, String text, boolean verifiedPurchase) {
        if (rating < 1 || rating > 5) {
            throw new IllegalArgumentException("Rating must be between 1 and 5");
        }

        this.reviewId = UUID.randomUUID().toString();
        this.author = author;
        this.product = product;
        this.rating = rating;
        this.title = title;
        this.text = text;
        this.verifiedPurchase = verifiedPurchase;
        this.createdAt = LocalDateTime.now();
        this.helpfulCount = 0;
    }

    public void markHelpful() {
        helpfulCount++;
    }

    public boolean isVerified() {
        return verifiedPurchase;
    }

    public int getRating() { return rating; }
    public String getTitle() { return title; }
    public String getText() { return text; }
}

class Rating {
    private Product product;
    private double averageRating;
    private int totalReviews;
    private Map<Integer, Integer> ratingDistribution; // star count -> number of reviews

    public Rating(Product product) {
        this.product = product;
        this.averageRating = 0.0;
        this.totalReviews = 0;
        this.ratingDistribution = new HashMap<>();
        for (int i = 1; i <= 5; i++) {
            ratingDistribution.put(i, 0);
        }
    }

    public void addRating(int rating) {
        totalReviews++;
        ratingDistribution.merge(rating, 1, Integer::sum);
        calculateAverage();
    }

    public double calculateAverage() {
        if (totalReviews == 0) {
            return 0.0;
        }

        int totalStars = 0;
        for (Map.Entry<Integer, Integer> entry : ratingDistribution.entrySet()) {
            totalStars += entry.getKey() * entry.getValue();
        }

        this.averageRating = (double) totalStars / totalReviews;
        return averageRating;
    }

    public double getAverageRating() { return averageRating; }
    public int getTotalReviews() { return totalReviews; }
    public Map<Integer, Integer> getRatingDistribution() { return ratingDistribution; }
}

// ============================================================================
// Repository Interfaces
// ============================================================================

interface ProductRepository {
    List<Product> search(SearchCriteria criteria);
    Product findById(String productId);
    void save(Product product);
    List<Product> findByCategory(String categoryId);
    List<Product> findTopRated(int limit);
}

interface OrderRepository {
    Order findById(String orderId);
    List<Order> findByCustomer(String customerId);
    void save(Order order);
    List<Order> findByStatus(OrderStatus status);
}

interface UserRepository {
    User findById(String userId);
    User findByEmail(String email);
    void save(User user);
    Member authenticate(String email, String password);
}

// ============================================================================
// Notification Service (Observer Pattern)
// ============================================================================

class NotificationService {
    private List<NotificationChannel> channels;

    public NotificationService() {
        this.channels = new ArrayList<>();
    }

    public void addChannel(NotificationChannel channel) {
        channels.add(channel);
    }

    public void notifyOrderPlaced(Order order) {
        String message = "Your order " + order.getOrderId() + " has been placed successfully!";
        sendNotification(order.getCustomer(), "Order Confirmation", message);
    }

    public void notifyOrderShipped(Order order, Shipment shipment) {
        String message = "Your order " + order.getOrderId() + " has been shipped! " +
                        "Tracking: " + shipment.getTrackingNumber();
        sendNotification(order.getCustomer(), "Order Shipped", message);
    }

    public void notifyOrderDelivered(Order order) {
        String message = "Your order " + order.getOrderId() + " has been delivered!";
        sendNotification(order.getCustomer(), "Order Delivered", message);
    }

    private void sendNotification(User user, String subject, String message) {
        for (NotificationChannel channel : channels) {
            channel.send(user, subject, message);
        }
    }
}

interface NotificationChannel {
    void send(User user, String subject, String message);
}

class EmailNotification implements NotificationChannel {
    @Override
    public void send(User user, String subject, String message) {
        System.out.println("Sending email to " + user.getEmail());
        System.out.println("Subject: " + subject);
        System.out.println("Message: " + message);
    }
}

class SMSNotification implements NotificationChannel {
    @Override
    public void send(User user, String subject, String message) {
        System.out.println("Sending SMS to " + user.phoneNumber);
        System.out.println("Message: " + message);
    }
}

Python

from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime, timedelta
from decimal import Decimal
from enum import Enum
from typing import List, Optional, Dict, Set
from uuid import uuid4


# ============================================================================
# Enums
# ============================================================================

class OrderStatus(Enum):
    PENDING = "pending"
    PAYMENT_AUTHORIZED = "payment_authorized"
    PROCESSING = "processing"
    SHIPPED = "shipped"
    DELIVERED = "delivered"
    COMPLETED = "completed"
    CANCELLED = "cancelled"
    REFUNDED = "refunded"


class ListingStatus(Enum):
    DRAFT = "draft"
    SUBMITTED = "submitted"
    APPROVED = "approved"
    REJECTED = "rejected"
    INACTIVE = "inactive"


class ShipmentStatus(Enum):
    PENDING = "pending"
    PICKED_UP = "picked_up"
    IN_TRANSIT = "in_transit"
    OUT_FOR_DELIVERY = "out_for_delivery"
    DELIVERED = "delivered"
    FAILED_DELIVERY = "failed_delivery"
    RETURNED = "returned"


class PaymentStatus(Enum):
    PENDING = "pending"
    AUTHORIZED = "authorized"
    CAPTURED = "captured"
    FAILED = "failed"
    REFUNDED = "refunded"


class AccountType(Enum):
    GUEST = "guest"
    MEMBER = "member"
    SELLER = "seller"
    ADMIN = "admin"


# ============================================================================
# Value Objects
# ============================================================================

@dataclass(frozen=True)
class Money:
    """Immutable money value object"""
    amount: Decimal
    currency: str = "USD"

    def __post_init__(self):
        if self.amount < 0:
            raise ValueError("Amount cannot be negative")

    def add(self, other: 'Money') -> 'Money':
        self._validate_currency(other)
        return Money(self.amount + other.amount, self.currency)

    def subtract(self, other: 'Money') -> 'Money':
        self._validate_currency(other)
        return Money(self.amount - other.amount, self.currency)

    def multiply(self, factor: float) -> 'Money':
        return Money(self.amount * Decimal(str(factor)), self.currency)

    def _validate_currency(self, other: 'Money') -> None:
        if self.currency != other.currency:
            raise ValueError("Currency mismatch")

    def __str__(self) -> str:
        return f"{self.currency} {self.amount}"


@dataclass
class SearchCriteria:
    keyword: str
    category_id: Optional[str] = None
    min_price: Optional[Money] = None
    max_price: Optional[Money] = None
    min_rating: Optional[float] = None
    page: int = 0
    page_size: int = 20


# ============================================================================
# User Management
# ============================================================================

class User(ABC):
    def __init__(self, user_id: str, email: Optional[str], name: str, account_type: AccountType) -> None:
        self.user_id = user_id
        self.email = email
        self.name = name
        self.phone_number: Optional[str] = None
        self.addresses: List['ShippingAddress'] = []
        self.account_type = account_type

    @abstractmethod
    def can_purchase(self) -> bool:
        pass

    def add_address(self, address: 'ShippingAddress') -> None:
        self.addresses.append(address)


class Guest(User):
    def __init__(self, session_id: str) -> None:
        super().__init__(f"guest-{session_id}", None, "Guest User", AccountType.GUEST)
        self.session_id = session_id
        self.cart = ShoppingCart(self)

    def can_purchase(self) -> bool:
        return False

    def register(self, email: str, password: str, name: str) -> 'Member':
        return Member(str(uuid4()), email, password, name)


class Member(User):
    def __init__(self, user_id: str, email: str, password_hash: str, name: str) -> None:
        super().__init__(user_id, email, name, AccountType.MEMBER)
        self.password_hash = password_hash
        self.order_history: List['Order'] = []
        self.wishlist = Wishlist(self)
        self.saved_payment_methods: List['PaymentMethod'] = []
        self.registered_at = datetime.now()

    def can_purchase(self) -> bool:
        return True

    def place_order(self, cart: 'ShoppingCart', address: 'ShippingAddress',
                   payment: 'PaymentMethod') -> 'Order':
        order = Order(self, cart, address)
        self.order_history.append(order)
        return order

    def write_review(self, product: 'Product', rating: int,
                    title: str, text: str) -> 'Review':
        verified_purchase = any(
            item.product.product_id == product.product_id
            for order in self.order_history
            for item in order.items
        )
        return Review(self, product, rating, title, text, verified_purchase)

    def add_payment_method(self, method: 'PaymentMethod') -> None:
        self.saved_payment_methods.append(method)


class Seller(User):
    def __init__(self, user_id: str, email: str, business_name: str, tax_id: str) -> None:
        super().__init__(user_id, email, business_name, AccountType.SELLER)
        self.business_name = business_name
        self.tax_id = tax_id
        self.listings: List['ProductListing'] = []
        self.analytics = SalesAnalytics(self)

    def can_purchase(self) -> bool:
        return True

    def create_listing(self, product: 'Product', initial_stock: int) -> 'ProductListing':
        listing = ProductListing(self, product, initial_stock)
        self.listings.append(listing)
        return listing

    def update_inventory(self, listing: 'ProductListing', quantity: int, reason: str) -> None:
        listing.inventory.update_stock(quantity, reason)


class Admin(User):
    def __init__(self, user_id: str, email: str, name: str) -> None:
        super().__init__(user_id, email, name, AccountType.ADMIN)
        self.permissions: List[str] = []

    def can_purchase(self) -> bool:
        return True

    def approve_listing(self, listing: 'ProductListing') -> None:
        listing.approve()

    def reject_listing(self, listing: 'ProductListing', reason: str) -> None:
        listing.reject(reason)


class SalesAnalytics:
    def __init__(self, seller: Seller) -> None:
        self.seller = seller
        self.product_sales: Dict[str, int] = {}
        self.total_revenue = Money(Decimal('0'))

    def record_sale(self, product_id: str, amount: Money) -> None:
        self.product_sales[product_id] = self.product_sales.get(product_id, 0) + 1
        self.total_revenue = self.total_revenue.add(amount)


class Wishlist:
    def __init__(self, member: Member) -> None:
        self.wishlist_id = str(uuid4())
        self.member = member
        self.items: List['Product'] = []

    def add_item(self, product: 'Product') -> None:
        if product not in self.items:
            self.items.append(product)

    def remove_item(self, product: 'Product') -> None:
        self.items.remove(product)


# ============================================================================
# Product Catalog
# ============================================================================

class Product:
    def __init__(self, product_id: str, name: str, description: str,
                 base_price: Money, category: 'ProductCategory') -> None:
        self.product_id = product_id
        self.name = name
        self.description = description
        self.base_price = base_price
        self.category = category
        self.images: List['ProductImage'] = []
        self.attributes: List['ProductAttribute'] = []
        self.reviews: List['Review'] = []
        self.rating = Rating(self)

    def add_image(self, image: 'ProductImage') -> None:
        self.images.append(image)

    def add_attribute(self, key: str, value: str) -> None:
        self.attributes.append(ProductAttribute(key, value))

    def add_review(self, review: 'Review') -> None:
        self.reviews.append(review)
        self.rating.add_rating(review.rating)

    def get_average_rating(self) -> float:
        return self.rating.get_average_rating()


class ProductCategory:
    def __init__(self, category_id: str, name: str, parent: Optional['ProductCategory'] = None) -> None:
        self.category_id = category_id
        self.name = name
        self.parent = parent
        self.children: List['ProductCategory'] = []

        if parent:
            parent.add_child(self)

    def add_child(self, child: 'ProductCategory') -> None:
        self.children.append(child)

    def is_leaf_category(self) -> bool:
        return len(self.children) == 0

    def get_path(self) -> str:
        if self.parent is None:
            return self.name
        return f"{self.parent.get_path()} > {self.name}"


class ProductListing:
    def __init__(self, seller: Seller, product: Product, initial_stock: int) -> None:
        self.listing_id = str(uuid4())
        self.seller = seller
        self.product = product
        self.inventory = Inventory(self, initial_stock)
        self.status = ListingStatus.DRAFT
        self.created_at = datetime.now()
        self.rejection_reason: Optional[str] = None

    def submit(self) -> None:
        if self.status == ListingStatus.DRAFT:
            self.status = ListingStatus.SUBMITTED

    def approve(self) -> None:
        if self.status == ListingStatus.SUBMITTED:
            self.status = ListingStatus.APPROVED

    def reject(self, reason: str) -> None:
        if self.status == ListingStatus.SUBMITTED:
            self.status = ListingStatus.REJECTED
            self.rejection_reason = reason

    def is_available(self) -> bool:
        return self.status == ListingStatus.APPROVED and self.inventory.is_in_stock()


class Inventory:
    LOW_STOCK_THRESHOLD = 10

    def __init__(self, listing: ProductListing, initial_quantity: int) -> None:
        self.inventory_id = str(uuid4())
        self.listing = listing
        self.quantity = initial_quantity
        self.reserved_quantity = 0
        self.warehouse_location: Optional[str] = None
        self.transactions: List['InventoryTransaction'] = []

    def reserve(self, requested_quantity: int) -> bool:
        available = self.quantity - self.reserved_quantity
        if available >= requested_quantity:
            self.reserved_quantity += requested_quantity
            self.transactions.append(InventoryTransaction("RESERVED", requested_quantity))
            return True
        return False

    def release(self, release_quantity: int) -> None:
        self.reserved_quantity -= release_quantity
        self.transactions.append(InventoryTransaction("RELEASED", release_quantity))

    def update_stock(self, delta: int, reason: str) -> None:
        self.quantity += delta
        self.transactions.append(InventoryTransaction(reason, delta))

    def confirm_sale(self, sold_quantity: int) -> None:
        self.quantity -= sold_quantity
        self.reserved_quantity -= sold_quantity
        self.transactions.append(InventoryTransaction("SOLD", -sold_quantity))

    def is_in_stock(self) -> bool:
        return (self.quantity - self.reserved_quantity) > 0

    def is_low_stock(self) -> bool:
        return (self.quantity - self.reserved_quantity) <= self.LOW_STOCK_THRESHOLD

    def get_available_quantity(self) -> int:
        return self.quantity - self.reserved_quantity


class InventoryTransaction:
    def __init__(self, transaction_type: str, quantity_change: int) -> None:
        self.transaction_id = str(uuid4())
        self.type = transaction_type
        self.quantity_change = quantity_change
        self.timestamp = datetime.now()


@dataclass
class ProductAttribute:
    key: str
    value: str


@dataclass
class ProductImage:
    image_url: str
    display_order: int
    is_primary: bool


# ============================================================================
# Shopping Cart
# ============================================================================

class ShoppingCart:
    def __init__(self, user: User) -> None:
        self.cart_id = str(uuid4())
        self.user = user
        self.items: List['CartItem'] = []
        self.last_updated = datetime.now()

    def add_item(self, listing: ProductListing, quantity: int) -> None:
        existing_item = next(
            (item for item in self.items if item.listing.listing_id == listing.listing_id),
            None
        )

        if existing_item:
            existing_item.update_quantity(existing_item.quantity + quantity)
        else:
            self.items.append(CartItem(listing, quantity))

        self.last_updated = datetime.now()

    def remove_item(self, cart_item_id: str) -> None:
        self.items = [item for item in self.items if item.cart_item_id != cart_item_id]
        self.last_updated = datetime.now()

    def update_quantity(self, cart_item_id: str, new_quantity: int) -> None:
        for item in self.items:
            if item.cart_item_id == cart_item_id:
                item.update_quantity(new_quantity)
                break
        self.last_updated = datetime.now()

    def calculate_total(self, pricing_strategy: 'PricingStrategy') -> Money:
        total = Money(Decimal('0'))
        for item in self.items:
            item_price = pricing_strategy.calculate_price(item.listing.product, item.quantity)
            total = total.add(item_price)
        return total

    def clear(self) -> None:
        self.items.clear()
        self.last_updated = datetime.now()


class CartItem:
    def __init__(self, listing: ProductListing, quantity: int) -> None:
        self.cart_item_id = str(uuid4())
        self.listing = listing
        self.quantity = quantity
        self.price_snapshot = listing.product.base_price

    def update_quantity(self, new_quantity: int) -> None:
        if new_quantity <= 0:
            raise ValueError("Quantity must be positive")
        self.quantity = new_quantity

    def get_subtotal(self) -> Money:
        return self.price_snapshot.multiply(self.quantity)


# ============================================================================
# Orders
# ============================================================================

class Order:
    def __init__(self, customer: Member, cart: ShoppingCart, address: 'ShippingAddress') -> None:
        self.order_id = str(uuid4())
        self.customer = customer
        self.items: List['OrderItem'] = []
        self.status = OrderStatus.PENDING
        self.shipping_address = address
        self.created_at = datetime.now()
        self.status_history: List['OrderStatusHistory'] = []
        self.payment: Optional['Payment'] = None
        self.shipment: Optional['Shipment'] = None

        self.subtotal = Money(Decimal('0'))
        self.tax = Money(Decimal('0'))
        self.shipping_cost = Money(Decimal('0'))
        self.discount = Money(Decimal('0'))
        self.total = Money(Decimal('0'))

        # Convert cart items to order items
        for cart_item in cart.items:
            order_item = OrderItem(
                cart_item.listing.product,
                cart_item.quantity,
                cart_item.get_subtotal()
            )
            self.items.append(order_item)

        self._add_status_history(OrderStatus.PENDING)

    def calculate_total(self, pricing_strategy: 'PricingStrategy') -> Money:
        self.subtotal = Money(Decimal('0'))

        for item in self.items:
            item_price = pricing_strategy.calculate_price(item.product, item.quantity)
            self.subtotal = self.subtotal.add(item_price)

        # Calculate tax (10%)
        self.tax = self.subtotal.multiply(0.10)

        # Calculate shipping
        self.shipping_cost = self._calculate_shipping()

        # Total = subtotal + tax + shipping - discount
        self.total = self.subtotal.add(self.tax).add(self.shipping_cost).subtract(self.discount)

        return self.total

    def _calculate_shipping(self) -> Money:
        # Free shipping over $50
        if self.subtotal.amount >= Decimal('50'):
            return Money(Decimal('0'))
        return Money(Decimal('5.99'))

    def update_status(self, new_status: OrderStatus) -> None:
        self.status = new_status
        self._add_status_history(new_status)

    def _add_status_history(self, status: OrderStatus) -> None:
        self.status_history.append(OrderStatusHistory(status, datetime.now()))

    def cancel(self) -> bool:
        if self.can_cancel():
            self.update_status(OrderStatus.CANCELLED)
            return True
        return False

    def can_cancel(self) -> bool:
        return self.status in [OrderStatus.PENDING, OrderStatus.PAYMENT_AUTHORIZED]

    def apply_discount(self, discount_amount: Money) -> None:
        self.discount = discount_amount


@dataclass
class OrderItem:
    product: Product
    quantity: int
    price_at_purchase: Money
    order_item_id: str = None

    def __post_init__(self):
        if self.order_item_id is None:
            self.order_item_id = str(uuid4())

    def get_subtotal(self) -> Money:
        return self.price_at_purchase


@dataclass
class OrderStatusHistory:
    status: OrderStatus
    timestamp: datetime


# ============================================================================
# Pricing Strategy Pattern
# ============================================================================

class PricingStrategy(ABC):
    @abstractmethod
    def calculate_price(self, product: Product, quantity: int) -> Money:
        pass

    @abstractmethod
    def apply_discount(self, original_price: Money) -> Money:
        pass


class BasePricing(PricingStrategy):
    def calculate_price(self, product: Product, quantity: int) -> Money:
        return product.base_price.multiply(quantity)

    def apply_discount(self, original_price: Money) -> Money:
        return original_price


class PercentageDiscount(PricingStrategy):
    def __init__(self, discount_rate: float, base_strategy: PricingStrategy) -> None:
        self.discount_rate = discount_rate
        self.base_strategy = base_strategy

    def calculate_price(self, product: Product, quantity: int) -> Money:
        base_price = self.base_strategy.calculate_price(product, quantity)
        return self.apply_discount(base_price)

    def apply_discount(self, original_price: Money) -> Money:
        return original_price.multiply(1.0 - self.discount_rate)


class BulkDiscount(PricingStrategy):
    def __init__(self, base_strategy: PricingStrategy) -> None:
        self.base_strategy = base_strategy
        # quantity threshold -> discount rate
        self.tier_pricing = {
            3: 0.05,   # 3+ items = 5% off
            5: 0.10,   # 5+ items = 10% off
            10: 0.15   # 10+ items = 15% off
        }

    def calculate_price(self, product: Product, quantity: int) -> Money:
        base_price = self.base_strategy.calculate_price(product, quantity)
        discount = self._get_discount_for_quantity(quantity)
        return base_price.multiply(1.0 - discount)

    def _get_discount_for_quantity(self, quantity: int) -> float:
        applicable_discounts = [
            rate for threshold, rate in self.tier_pricing.items()
            if quantity >= threshold
        ]
        return max(applicable_discounts) if applicable_discounts else 0.0

    def apply_discount(self, original_price: Money) -> Money:
        return original_price


class PromotionalCode(PricingStrategy):
    def __init__(self, code: str, base_strategy: PricingStrategy,
                 fixed_discount: Optional[Money] = None,
                 percentage_discount: Optional[float] = None,
                 expiry_date: Optional[datetime] = None) -> None:
        self.code = code
        self.base_strategy = base_strategy
        self.fixed_discount = fixed_discount
        self.percentage_discount = percentage_discount
        self.expiry_date = expiry_date

    def is_valid(self) -> bool:
        if self.expiry_date:
            return datetime.now() < self.expiry_date
        return True

    def calculate_price(self, product: Product, quantity: int) -> Money:
        if not self.is_valid():
            return self.base_strategy.calculate_price(product, quantity)

        base_price = self.base_strategy.calculate_price(product, quantity)
        return self.apply_discount(base_price)

    def apply_discount(self, original_price: Money) -> Money:
        if not self.is_valid():
            return original_price

        if self.fixed_discount:
            discounted = original_price.subtract(self.fixed_discount)
            return discounted if discounted.amount > 0 else Money(Decimal('0'))
        elif self.percentage_discount:
            return original_price.multiply(1.0 - self.percentage_discount)
        return original_price


# ============================================================================
# Payment
# ============================================================================

@dataclass
class PaymentResult:
    success: bool
    transaction_id: Optional[str]
    error_message: Optional[str]


class Payment:
    def __init__(self, amount: Money, method: 'PaymentMethod') -> None:
        self.payment_id = str(uuid4())
        self.amount = amount
        self.method = method
        self.status = PaymentStatus.PENDING
        self.transaction_id: Optional[str] = None
        self.timestamp = datetime.now()

    def authorize(self) -> bool:
        result = self.method.process_payment(self.amount)
        if result.success:
            self.status = PaymentStatus.AUTHORIZED
            self.transaction_id = result.transaction_id
            return True
        self.status = PaymentStatus.FAILED
        return False

    def capture(self) -> bool:
        if self.status == PaymentStatus.AUTHORIZED:
            self.status = PaymentStatus.CAPTURED
            return True
        return False

    def refund(self, refund_amount: Money) -> bool:
        if self.status == PaymentStatus.CAPTURED and self.transaction_id:
            success = self.method.refund(self.transaction_id, refund_amount)
            if success:
                self.status = PaymentStatus.REFUNDED
                return True
        return False


class PaymentMethod(ABC):
    @abstractmethod
    def process_payment(self, amount: Money) -> PaymentResult:
        pass

    @abstractmethod
    def refund(self, transaction_id: str, amount: Money) -> bool:
        pass


class CreditCardPayment(PaymentMethod):
    def __init__(self, card_number: str, cvv: str, expiry_date: str, card_holder_name: str) -> None:
        self.card_number = self._mask_card_number(card_number)
        self.cvv = cvv
        self.expiry_date = expiry_date
        self.card_holder_name = card_holder_name

    def _mask_card_number(self, card_number: str) -> str:
        if len(card_number) < 4:
            return card_number
        return "**** **** **** " + card_number[-4:]

    def process_payment(self, amount: Money) -> PaymentResult:
        # Simulate payment gateway call
        transaction_id = f"TXN-{uuid4()}"
        return PaymentResult(True, transaction_id, None)

    def refund(self, transaction_id: str, amount: Money) -> bool:
        return True


class PayPalPayment(PaymentMethod):
    def __init__(self, paypal_email: str, auth_token: str) -> None:
        self.paypal_email = paypal_email
        self.auth_token = auth_token

    def process_payment(self, amount: Money) -> PaymentResult:
        transaction_id = f"PP-{uuid4()}"
        return PaymentResult(True, transaction_id, None)

    def refund(self, transaction_id: str, amount: Money) -> bool:
        return True


class GiftCardPayment(PaymentMethod):
    def __init__(self, card_number: str, balance: Money) -> None:
        self.card_number = card_number
        self.balance = balance

    def process_payment(self, amount: Money) -> PaymentResult:
        if self.balance.amount >= amount.amount:
            self.balance = self.balance.subtract(amount)
            transaction_id = f"GC-{uuid4()}"
            return PaymentResult(True, transaction_id, None)
        return PaymentResult(False, None, "Insufficient gift card balance")

    def refund(self, transaction_id: str, amount: Money) -> bool:
        self.balance = self.balance.add(amount)
        return True

    def check_balance(self) -> Money:
        return self.balance


# ============================================================================
# Shipping
# ============================================================================

class Shipment:
    def __init__(self, order: Order, carrier: str, destination: 'ShippingAddress') -> None:
        self.shipment_id = str(uuid4())
        self.order = order
        self.carrier = carrier
        self.tracking_number = self._generate_tracking_number()
        self.destination = destination
        self.status = ShipmentStatus.PENDING
        self.events: List['ShipmentEvent'] = []
        self.estimated_delivery = datetime.now() + timedelta(days=5)

    def _generate_tracking_number(self) -> str:
        return f"TRACK-{int(datetime.now().timestamp())}"

    def update_status(self, new_status: ShipmentStatus) -> None:
        self.status = new_status
        self.add_event(ShipmentEvent(new_status, "Status updated", "Warehouse"))

    def add_event(self, event: 'ShipmentEvent') -> None:
        self.events.append(event)

    def get_latest_event(self) -> Optional['ShipmentEvent']:
        return self.events[-1] if self.events else None


@dataclass
class ShipmentEvent:
    status: ShipmentStatus
    description: str
    location: str
    timestamp: datetime = None

    def __post_init__(self):
        if self.timestamp is None:
            self.timestamp = datetime.now()


class ShippingAddress:
    def __init__(self, recipient_name: str, address_line1: str, city: str,
                 state: str, postal_code: str, country: str) -> None:
        self.recipient_name = recipient_name
        self.address_line1 = address_line1
        self.address_line2: Optional[str] = None
        self.city = city
        self.state = state
        self.postal_code = postal_code
        self.country = country
        self.phone_number: Optional[str] = None

    def validate(self) -> bool:
        return all([
            self.recipient_name,
            self.address_line1,
            self.city,
            self.postal_code,
            self.country
        ])

    def format(self) -> str:
        lines = [
            self.recipient_name,
            self.address_line1
        ]
        if self.address_line2:
            lines.append(self.address_line2)
        lines.append(f"{self.city}, {self.state} {self.postal_code}")
        lines.append(self.country)
        return "\n".join(lines)


# ============================================================================
# Reviews & Ratings
# ============================================================================

class Review:
    def __init__(self, author: Member, product: Product, rating: int,
                 title: str, text: str, verified_purchase: bool) -> None:
        if not 1 <= rating <= 5:
            raise ValueError("Rating must be between 1 and 5")

        self.review_id = str(uuid4())
        self.author = author
        self.product = product
        self.rating = rating
        self.title = title
        self.text = text
        self.verified_purchase = verified_purchase
        self.created_at = datetime.now()
        self.helpful_count = 0

    def mark_helpful(self) -> None:
        self.helpful_count += 1

    def is_verified(self) -> bool:
        return self.verified_purchase


class Rating:
    def __init__(self, product: Product) -> None:
        self.product = product
        self.average_rating = 0.0
        self.total_reviews = 0
        self.rating_distribution: Dict[int, int] = {i: 0 for i in range(1, 6)}

    def add_rating(self, rating: int) -> None:
        self.total_reviews += 1
        self.rating_distribution[rating] += 1
        self.calculate_average()

    def calculate_average(self) -> float:
        if self.total_reviews == 0:
            return 0.0

        total_stars = sum(stars * count for stars, count in self.rating_distribution.items())
        self.average_rating = total_stars / self.total_reviews
        return self.average_rating

    def get_average_rating(self) -> float:
        return self.average_rating


# ============================================================================
# Repository Interfaces
# ============================================================================

class ProductRepository(ABC):
    @abstractmethod
    def search(self, criteria: SearchCriteria) -> List[Product]:
        pass

    @abstractmethod
    def find_by_id(self, product_id: str) -> Optional[Product]:
        pass

    @abstractmethod
    def save(self, product: Product) -> None:
        pass

    @abstractmethod
    def find_by_category(self, category_id: str) -> List[Product]:
        pass


class OrderRepository(ABC):
    @abstractmethod
    def find_by_id(self, order_id: str) -> Optional[Order]:
        pass

    @abstractmethod
    def find_by_customer(self, customer_id: str) -> List[Order]:
        pass

    @abstractmethod
    def save(self, order: Order) -> None:
        pass

    @abstractmethod
    def find_by_status(self, status: OrderStatus) -> List[Order]:
        pass


class UserRepository(ABC):
    @abstractmethod
    def find_by_id(self, user_id: str) -> Optional[User]:
        pass

    @abstractmethod
    def find_by_email(self, email: str) -> Optional[User]:
        pass

    @abstractmethod
    def save(self, user: User) -> None:
        pass

    @abstractmethod
    def authenticate(self, email: str, password: str) -> Optional[Member]:
        pass


# ============================================================================
# Notification Service (Observer Pattern)
# ============================================================================

class NotificationService:
    def __init__(self) -> None:
        self.channels: List['NotificationChannel'] = []

    def add_channel(self, channel: 'NotificationChannel') -> None:
        self.channels.append(channel)

    def notify_order_placed(self, order: Order) -> None:
        message = f"Your order {order.order_id} has been placed successfully!"
        self._send_notification(order.customer, "Order Confirmation", message)

    def notify_order_shipped(self, order: Order, shipment: Shipment) -> None:
        message = f"Your order {order.order_id} has been shipped! Tracking: {shipment.tracking_number}"
        self._send_notification(order.customer, "Order Shipped", message)

    def notify_order_delivered(self, order: Order) -> None:
        message = f"Your order {order.order_id} has been delivered!"
        self._send_notification(order.customer, "Order Delivered", message)

    def _send_notification(self, user: User, subject: str, message: str) -> None:
        for channel in self.channels:
            channel.send(user, subject, message)


class NotificationChannel(ABC):
    @abstractmethod
    def send(self, user: User, subject: str, message: str) -> None:
        pass


class EmailNotification(NotificationChannel):
    def send(self, user: User, subject: str, message: str) -> None:
        print(f"Sending email to {user.email}")
        print(f"Subject: {subject}")
        print(f"Message: {message}")


class SMSNotification(NotificationChannel):
    def send(self, user: User, subject: str, message: str) -> None:
        print(f"Sending SMS to {user.phone_number}")
        print(f"Message: {message}")

This implementation demonstrates:

  • SOLID Principles thoroughly applied across all classes
  • Strategy Pattern for pricing with multiple discount strategies (percentage, bulk, promotional codes)
  • Strategy Pattern for payment methods (credit card, PayPal, gift card)
  • Repository Pattern for clean data access abstraction
  • Observer Pattern for notifications across multiple channels
  • State Pattern for order status management
  • Value Object Pattern for Money with immutability and currency safety
  • Rich domain models with comprehensive business logic
  • Inventory management with reservation system to prevent overselling
  • Complete e-commerce workflow from browsing to delivery
  • Review and rating system with verified purchase tracking
  • Multi-role user system (Guest, Member, Seller, Admin)
  • Comprehensive type hints and documentation

Comments