Try Our Course for 4 Hours @99rs

Try our course for 4 Hours and if you like it, you can go for one year or lifetime access. If you buy our (1yr or lifetime) course 99rs will be refunded !

Design (LLD) Coupon System For Zepto - Machine Coding Interview

Design (LLD) Coupon System For Zepto - Machine Coding Interview

Asked in LLD Interview for SDE-2

Problem Statement:

Design a Coupon Recommendation System for Zepto that fulfills the following requirements:

Key Requirements:

  1. 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.

  2. Concurrency:

    • Ensure thread safety for rule evaluation when multiple users access the system.
  3. Optimization:

    • Optimize the rule evaluation process to minimize redundant checks.

Concurrency Requirements:

  1. Thread safety for concurrent requests.

  2. 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()));
    }
}

Above Solution is not a multi-threaded solution, that one will be covered in our course with Video Explanation.