Functional Specifications (In-Scope)
- Guest vs Logged-In Modes: Decouples anonymous session carts stored in-memory from user account carts stored in the user profile registry.
- Pluggable Conflict Resolution: Configurable merge policies (
KEEP_GUEST,KEEP_USER,SUM_QUANTITIES,PREFER_HIGHER_QTY). - Real-Time Inventory Validation: Verifies stock balances on merge, automatically downsizing or deleting out-of-stock items.
- User Alert Dispatcher: Captures stock discrepancy modifications to push notification alerts to logged-in users.
Out-of-Scope Boundaries
- Payment Integration Gateways: Excludes checkout transactions to focus strictly on session and inventory reconciliations.
- Persistent Cookie Store TTLs: Relies on simple React states to represent short-term cookie memory.
Production reference implementations demonstrating conflict solvers, inventory integrations, and clean cleanup mechanisms in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
enum MergePolicy {
KEEP_GUEST,
KEEP_USER,
SUM_QUANTITIES,
PREFER_HIGHER_QTY
}
class CartItem {
private final String productId;
private int quantity;
private final double unitPrice;
private final long addedAt;
public CartItem(String productId, int quantity, double unitPrice) {
this.productId = productId;
this.quantity = quantity;
this.unitPrice = unitPrice;
this.addedAt = System.currentTimeMillis();
}
public String getProductId() { return productId; }
public int getQuantity() { return quantity; }
public double getUnitPrice() { return unitPrice; }
public long getAddedAt() { return addedAt; }
public void setQuantity(int quantity) { this.quantity = quantity; }
}
class Cart {
private final String cartId; // sessionId or userId
private final Map<String, CartItem> items = new ConcurrentHashMap<>();
private long lastUpdated;
public Cart(String cartId) {
this.cartId = cartId;
this.lastUpdated = System.currentTimeMillis();
}
public String getCartId() { return cartId; }
public Map<String, CartItem> getItems() { return items; }
public long getLastUpdated() { return lastUpdated; }
public void addItem(String productId, int qty, double price) {
items.merge(productId, new CartItem(productId, qty, price), (oldVal, newVal) -> {
oldVal.setQuantity(oldVal.getQuantity() + qty);
return oldVal;
});
lastUpdated = System.currentTimeMillis();
}
public void clear() {
items.clear();
lastUpdated = System.currentTimeMillis();
}
}
class InventoryService {
private final Map<String, Integer> stockRegistry = new ConcurrentHashMap<>();
public void setStock(String productId, int qty) {
stockRegistry.put(productId, qty);
}
public int getAvailableStock(String productId) {
return stockRegistry.getOrDefault(productId, 0);
}
}
class MergeResult {
private final Cart mergedCart;
private final List<String> notifications;
public MergeResult(Cart mergedCart, List<String> notifications) {
this.mergedCart = mergedCart;
this.notifications = notifications;
}
public Cart getMergedCart() { return mergedCart; }
public List<String> getNotifications() { return notifications; }
}
class CartMergeService {
private final Map<String, Cart> guestCarts = new ConcurrentHashMap<>();
private final Map<String, Cart> userCarts = new ConcurrentHashMap<>();
private final InventoryService inventoryService;
public CartMergeService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
public Cart getGuestCart(String sessionId) {
return guestCarts.computeIfAbsent(sessionId, Cart::new);
}
public Cart getUserCart(String userId) {
return userCarts.computeIfAbsent(userId, Cart::new);
}
public MergeResult merge(String sessionId, String userId, MergePolicy policy) {
Cart guestCart = guestCarts.get(sessionId);
Cart userCart = getUserCart(userId);
List<String> notifications = new ArrayList<>();
if (guestCart == null || guestCart.getItems().isEmpty()) {
return new MergeResult(userCart, notifications);
}
// Apply merge logic
for (CartItem guestItem : guestCart.getItems().values()) {
String prodId = guestItem.getProductId();
CartItem userItem = userCart.getItems().get(prodId);
int targetQty = 0;
if (userItem == null) {
targetQty = guestItem.getQuantity();
} else {
switch (policy) {
case KEEP_GUEST:
targetQty = guestItem.getQuantity();
break;
case KEEP_USER:
targetQty = userItem.getQuantity();
break;
case SUM_QUANTITIES:
targetQty = userItem.getQuantity() + guestItem.getQuantity();
break;
case PREFER_HIGHER_QTY:
targetQty = Math.max(userItem.getQuantity(), guestItem.getQuantity());
break;
}
}
// Inventory Validation Check
int availableStock = inventoryService.getAvailableStock(prodId);
if (availableStock <= 0) {
notifications.add("Removed product: " + prodId + " (Out of Stock)");
userCart.getItems().remove(prodId);
} else if (targetQty > availableStock) {
notifications.add("Reduced qty of: " + prodId + " from " + targetQty + " to " + availableStock + " due to limited stock.");
if (userItem != null) {
userItem.setQuantity(availableStock);
} else {
userCart.addItem(prodId, availableStock, guestItem.getUnitPrice());
}
} else {
if (userItem != null) {
userItem.setQuantity(targetQty);
} else {
userCart.addItem(prodId, targetQty, guestItem.getUnitPrice());
}
}
}
// Clean up Session Guest Cart
guestCarts.remove(sessionId);
return new MergeResult(userCart, notifications);
}
}
public class Main {
public static void main(String[] args) {
System.out.println("=== JAVA SHOPPING CART MERGE SIMULATION ===");
InventoryService inventory = new InventoryService();
inventory.setStock("laptop", 2);
inventory.setStock("headphones", 0);
inventory.setStock("keyboard", 5);
CartMergeService service = new CartMergeService(inventory);
// Guest session adding items
String sessionId = "guest-session-999";
Cart guestCart = service.getGuestCart(sessionId);
guestCart.addItem("laptop", 3, 1200.0);
guestCart.addItem("headphones", 1, 250.0);
// Authenticated user cart has some existing items
String userId = "user-alice";
Cart userCart = service.getUserCart(userId);
userCart.addItem("laptop", 1, 1200.0);
userCart.addItem("keyboard", 1, 150.0);
System.out.println("Guest Cart Qty of laptop: " + guestCart.getItems().get("laptop").getQuantity());
System.out.println("User Cart Qty of laptop: " + userCart.getItems().get("laptop").getQuantity());
// Perform merge
System.out.println("\nLogging in guest to merge carts with PREFER_HIGHER_QTY policy...");
MergeResult result = service.merge(sessionId, userId, MergePolicy.PREFER_HIGHER_QTY);
System.out.println("Merge complete. Notifications:");
for (String note : result.getNotifications()) {
System.out.println(" - " + note);
}
System.out.println("\nFinal Merged User Cart Items:");
for (CartItem item : result.getMergedCart().getItems().values()) {
System.out.println("Product: " + item.getProductId() + ", Qty: " + item.getQuantity() + ", Price: $" + item.getUnitPrice());
}
System.out.println("=== END OF JAVA SIMULATION ===");
}
}