Asked in LLD Interview for SDE-2
Problem Statement:
Design a Coupon Recommendation System for Zepto that fulfills the following requirements:
Key Requirements:
Coupon Recommendation:
Recommend applicable coupons for a given order.
Evaluate rules dynamically based on the user's order.
Support conditions like product type, user type, and payment method.
Concurrency:
- Ensure thread safety for rule evaluation when multiple users access the system.
Optimization:
- Optimize the rule evaluation process to minimize redundant checks.
Concurrency Requirements:
Thread safety for concurrent requests.
Efficient use of multi-threading during rule evaluations.
Solution
class User {
private String userId;
private String userType; // e.g., Regular, Premium or we can use enum also here.
private List<String> pastPurchases;
public User(String userId, String userType, List<String> pastPurchases) {
this.userId = userId;
this.userType = userType;
this.pastPurchases = pastPurchases;
}
public String getUserType() {
return userType;
}
public List<String> getPastPurchases() {
return pastPurchases;
}
}
class CartItem {
private String productId;
private String productType; // e.g., Groceries, Beverages, we can use enum also
private int quantity;
private double price;
public CartItem(String productId, String productType, int quantity, double price) {
this.productId = productId;
this.productType = productType;
this.quantity = quantity;
this.price = price;
}
public String getProductType() {
return productType;
}
public double getPrice() {
return price;
}
}
class Cart {
private String cartId;
private List<CartItem> items;
public Cart(String cartId, List<CartItem> items) {
this.cartId = cartId;
this.items = items;
}
public List<CartItem> getItems() {
return items;
}
}
class Order {
private String orderId;
private User user;
private Cart cart;
private double totalAmount;
public Order(String orderId, User user, Cart cart, double totalAmount) {
this.orderId = orderId;
this.user = user;
this.cart = cart;
this.totalAmount = totalAmount;
}
public User getUser() {
return user;
}
public Cart getCart() {
return cart;
}
public double getTotalAmount() {
return totalAmount;
}
}
class Coupon {
private String couponId;
private double discountAmount;
private List<Rule> rules;
private String rulesFormula; // Logical formula, e.g., "AND(0, 1)"
public Coupon(String couponId, double discountAmount, List<Rule> rules, String rulesFormula) {
this.couponId = couponId;
this.discountAmount = discountAmount;
this.rules = rules;
this.rulesFormula = rulesFormula;
}
public double getDiscountAmount() {
return discountAmount;
}
public boolean isApplicable(Order order) {
for (Rule rule : rules) {
if (!rule.check(order)) {
return false;
}
}
return true;
}
}
interface Rule {
boolean check(Order order);
}
class UserTypeRule implements Rule {
private String allowedUserType;
public UserTypeRule(String allowedUserType) {
this.allowedUserType = allowedUserType;
}
@Override
public boolean check(Order order) {
return order.getUser().getUserType().equals(allowedUserType);
}
}
class ProductTypeRule implements Rule {
private String requiredProductType;
public ProductTypeRule(String requiredProductType) {
this.requiredProductType = requiredProductType;
}
@Override
public boolean check(Order order) {
for (CartItem item : order.getCart().getItems()) {
if (item.getProductType().equals(requiredProductType)) {
return true;
}
}
return false;
}
}
interface CouponRecommendation {
List<Coupon> getCoupons(Order order);
}
class CouponRecommendationImpl implements CouponRecommendation {
private List<Coupon> availableCoupons;
public CouponRecommendationImpl(List<Coupon> availableCoupons) {
this.availableCoupons = availableCoupons;
}
@Override
public List<Coupon> getCoupons(Order order) {
List<Coupon> applicableCoupons = new ArrayList<>();
for (Coupon coupon : availableCoupons) {
if (coupon.isApplicable(order)) {
applicableCoupons.add(coupon);
}
}
return applicableCoupons;
}
}
public class Main {
public static void main(String[] args) {
// Create rules
Rule userTypeRule = new UserTypeRule("Premium");
Rule productTypeRule = new ProductTypeRule("Groceries");
// Create coupons
List<Rule> coupon1Rules = Arrays.asList(userTypeRule, productTypeRule);
Coupon coupon1 = new Coupon("C1", 100, coupon1Rules, "AND(0, 1)");
List<Rule> coupon2Rules = Arrays.asList(productTypeRule);
Coupon coupon2 = new Coupon("C2", 50, coupon2Rules, "0");
// Create data
User user = new User("U1", "Premium", new ArrayList<>());
CartItem item = new CartItem("P1", "Groceries", 2, 200);
Cart cart = new Cart("C1", Arrays.asList(item));
Order order = new Order("O1", user, cart, 400);
// Recommend coupons
CouponRecommendation recommendation = new CouponRecommendationImpl(Arrays.asList(coupon1, coupon2));
List<Coupon> coupons = recommendation.getCoupons(order);
// Print results
coupons.forEach(coupon -> System.out.println("Applicable Coupon: " + coupon.getDiscountAmount()));
}
}