OOD - Amazon-like Online Shopping System
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 typesGuest- Anonymous browsing, limited to viewing and cart operationsMember- Registered user with purchase history, wishlists, saved addressesSeller- Vendor account with listings, inventory, and sales analyticsAdmin- Platform administrator with moderation and catalog management capabilities
Product Catalog:
Product- Product entity with name, description, images, base price, categoryProductCategory- Hierarchical category structureProductListing- Seller's listing with inventory, pricing, and approval statusProductImage- Product image with URL and display orderProductAttribute- Key-value attributes (color, size, brand, etc.)
Shopping & Orders:
ShoppingCart- Session-based cart for guests and membersCartItem- Line item in cart with product, quantity, price snapshotOrder- Completed purchase with items, totals, payment, shipmentOrderItem- Line item in order with product snapshot and priceOrderStatus- State pattern for order lifecycle
Pricing Strategy Pattern:
PricingStrategy(interface) - Calculate final priceBasePricing- Standard product pricePercentageDiscount- 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, statusPaymentMethod(interface) - Strategy for different payment typesCreditCardPayment,PayPalPayment,GiftCardPayment- Concrete payment implementations
Inventory:
Inventory- Stock tracking with quantity, warehouse locationInventoryTransaction- Record of stock changes (added, sold, returned, damaged)
Shipping:
Shipment- Shipping information with carrier, tracking number, statusShippingAddress- Delivery address detailsShipmentEvent- Tracking events (picked up, in transit, delivered)
Reviews & Ratings:
Review- Customer review with rating, text, verified purchase flagRating- Numeric rating with aggregation methods
Notifications (Observer Pattern):
NotificationService- Manages notification deliveryEmailNotification,SMSNotification,PushNotification- Concrete notification types
Repositories (Repository Pattern):
ProductRepository- Product data access and searchOrderRepository- Order persistence and retrievalUserRepository- 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:
- Strategy Pattern:
PricingStrategyenables flexible pricing with discounts, promotions, bulk pricing - Strategy Pattern:
PaymentMethodallows multiple payment implementations - Repository Pattern: Abstracts data access for products, orders, users
- Observer Pattern:
NotificationServicenotifies users of order/shipment updates - State Pattern:
OrderStatusmanages order lifecycle transitions - Factory Pattern: Creating different user types (Guest, Member, Seller, Admin)
- 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
PaymentMethodorPricingStrategycan 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