Interview Experience for Senior Software Engineer at Razorpay: Machine Coding Round

Author Profile Pic
Anurag
Published on Sun Jun 30 2024 ~ 16 min read
Interview Experience for Senior Software Engineer at Razorpay: Machine Coding Round

Interviewing for a senior software engineering role, particularly at a fintech company like Razorpay, can be quite challenging. One of the key aspects of the interview process is the machine coding round, where you are required to demonstrate your coding skills and design abilities in real time. This blog will guide you through a real interview question with Razorpay and provide a detailed solution to help you prepare effectively.


Date of Interview - 24 June 2024

Duration - 1.30 Hours


Round 2 Interview Question


Design and implement an In-Memory Loan EMI Calculator.

Requirements:

  1. The system should support the creation of users who can either be customers or admins. Each user will have a unique identifier: username.
  2. Admins can create loans in the system for customers.
  3. When creating a loan, the following information is needed:
  4. admin_username
  5. customer_username
  6. principal amount
  7. interest rate
  8. loan tenure (in years)
  9. The interest for the loan is calculated using the formula:
  10. I = (P * N * R) / 100, where P is the principal amount, N is the number of years, and R is the rate of interest.
  11. The total amount to be repaid is A = P + I.
  12. The amount should be repaid monthly in the form of EMIs.
  13. Each EMI = A / (N * 12).
  14. Customers should be able to make EMI payments for their loans.
  15. Customers should be able to fetch information about their loans, including the details of all EMI payments made and the remaining EMIs.
  16. Admins should be able to fetch all loans for all customers.
  17. All functions should take the username as one of the arguments, and user-level validation should happen against this username.


Solution Overview


To design an effective and scalable solution, we'll use the following design patterns:

  • Factory Pattern: For creating users and loans.
  • Singleton Pattern: To ensure a single instance of the services.
  • Strategy Pattern: For handling different types of loan interest calculations (if needed in the future).


Project Structure

The project will be structured as follows:

src
└── com
    └── loan
        ├── main
        │   ├── LoanEMICalculator.java
        ├── models
        │   ├── User.java
        │   ├── Loan.java
        ├── services
        │   ├── UserService.java
        │   ├── LoanService.java
        ├── factories
        │   ├── UserFactory.java
        │   ├── LoanFactory.java
        └── strategies
            ├── InterestStrategy.java
            ├── SimpleInterestStrategy.java


Detailed Code Implementation

User.java (models package)


package com.loan.models;

public class User {
    private String username;
    private boolean isAdmin;

    public User(String username, boolean isAdmin) {
        this.username = username;
        this.isAdmin = isAdmin;
    }

    public String getUsername() {
        return username;
    }

    public boolean isAdmin() {
        return isAdmin;
    }
}


Loan.java (models package)


package com.loan.models;

import com.loan.strategies.InterestStrategy;

import java.util.ArrayList;
import java.util.List;

public class Loan {
    private String adminUsername;
    private String customerUsername;
    private double principalAmount;
    private double interestRate;
    private int loanTenure;
    private List<Double> emiPayments;
    private double remainingPrincipal;

    public Loan(String adminUsername, String customerUsername, double principalAmount, double interestRate, int loanTenure, InterestStrategy interestStrategy) {
        this.adminUsername = adminUsername;
        this.customerUsername = customerUsername;
        this.principalAmount = principalAmount;
        this.interestRate = interestRate;
        this.loanTenure = loanTenure;
        this.emiPayments = new ArrayList<>();
        calculateEmiPayments(interestStrategy);
    }

    private void calculateEmiPayments(InterestStrategy interestStrategy) {
        double interest = interestStrategy.calculateInterest(principalAmount, interestRate, loanTenure);
        double totalAmount = principalAmount + interest;
        double emi = totalAmount / (loanTenure * 12);
        for (int i = 0; i < loanTenure * 12; i++) {
            emiPayments.add(emi);
        }
        remainingPrincipal = totalAmount;
    }

    public void makeEmiPayment(double amount) {
        if (remainingPrincipal > 0 && !emiPayments.isEmpty()) {
            emiPayments.remove(0);
            remainingPrincipal -= amount;
        }
    }

    public void fetchLoanInfo() {
        System.out.println("Loan Details:");
        System.out.println("Admin Username: " + adminUsername);
        System.out.println("Customer Username: " + customerUsername);
        System.out.println("Principal Amount: " + principalAmount);
        System.out.println("Interest Rate: " + interestRate);
        System.out.println("Loan Tenure: " + loanTenure);
        System.out.println("EMI Payments Done: " + (loanTenure * 12 - emiPayments.size()));
        System.out.println("Remaining Principal: " + remainingPrincipal);
        System.out.println("Remaining EMIs: " + emiPayments.size());
    }

    public String getCustomerUsername() {
        return customerUsername;
    }
}


UserService.java (services package)


package com.loan.services;

import com.loan.models.User;

import java.util.HashMap;
import java.util.Map;

public class UserService {
    private static UserService instance;
    private Map<String, User> users;

    private UserService() {
        users = new HashMap<>();
    }

    public static synchronized UserService getInstance() {
        if (instance == null) {
            instance = new UserService();
        }
        return instance;
    }

    public void createUser(String username, boolean isAdmin) {
        if (!users.containsKey(username)) {
            users.put(username, new User(username, isAdmin));
            System.out.println("User created successfully.");
        } else {
            System.out.println("User with username " + username + " already exists.");
        }
    }

    public User getUser(String username) {
        return users.get(username);
    }
}


LoanService.java (services package)

package com.loan.services;

import com.loan.models.Loan;
import com.loan.models.User;
import com.loan.strategies.InterestStrategy;

import java.util.*;

public class LoanService {
    private static LoanService instance;
    private Map<String, List<Loan>> loans;
    private UserService userService;

    private LoanService() {
        loans = new HashMap<>();
        userService = UserService.getInstance();
    }

    public static synchronized LoanService getInstance() {
        if (instance == null) {
            instance = new LoanService();
        }
        return instance;
    }

    public void createLoan(String adminUsername, String customerUsername, double principalAmount, double interestRate, int loanTenure, InterestStrategy interestStrategy) {
        User admin = userService.getUser(adminUsername);
        if (admin != null && admin.isAdmin()) {
            Loan newLoan = new Loan(adminUsername, customerUsername, principalAmount, interestRate, loanTenure, interestStrategy);
            loans.computeIfAbsent(customerUsername, k -> new ArrayList<>()).add(newLoan);
            System.out.println("Loan created successfully.");
        } else {
            System.out.println("Admin with username " + adminUsername + " not found or unauthorized.");
        }
    }

    public void makeEmiPayment(String customerUsername, double amount) {
        List<Loan> customerLoans = loans.get(customerUsername);
        if (customerLoans != null && !customerLoans.isEmpty()) {
            for (Loan loan : customerLoans) {
                loan.makeEmiPayment(amount);
            }
            System.out.println("EMI payment successful.");
        } else {
            System.out.println("No active loan found for customer " + customerUsername);
        }
    }

    public void fetchLoanInfo(String customerUsername, String requestingUsername) {
        if (!customerUsername.equals(requestingUsername)) {
            System.out.println("Access denied. You are not authorized to fetch loan info for this user.");
            return;
        }

        List<Loan> customerLoans = loans.get(customerUsername);
        if (customerLoans != null && !customerLoans.isEmpty()) {
            for (Loan loan : customerLoans) {
                loan.fetchLoanInfo();
                System.out.println("--------------------------");
            }
        } else {
            System.out.println("No active loan found for customer " + customerUsername);
        }
    }

    public void fetchAllLoans(String adminUsername) {
        User admin = userService.getUser(adminUsername);
        if (admin != null && admin.isAdmin()) {
            for (List<Loan> customerLoans : loans.values()) {
                for (Loan loan : customerLoans) {
                    loan.fetchLoanInfo();
                    System.out.println("--------------------------");
                }
            }
        } else {
            System.out.println("Admin with username " + adminUsername + " not found or unauthorized.");
        }
    }
}


UserFactory.java (factories package)


package com.loan.factories;

import com.loan.models.User;

public class UserFactory {

    public static User createUser(String username, boolean isAdmin) {
        return new User(username, isAdmin);
    }
}


LoanFactory.java (factories package)


package com.loan.factories;

import com.loan.models.Loan;
import com.loan.strategies.InterestStrategy;

public class LoanFactory {

    public static Loan createLoan(String adminUsername, String customerUsername, double principalAmount, double interestRate, int loanTenure, InterestStrategy interestStrategy) {
        return new Loan(adminUsername, customerUsername, principalAmount, interestRate, loanTenure, interestStrategy);
    }
}


InterestStrategy.java (strategies package)


package com.loan.strategies;

public interface InterestStrategy {
    double calculateInterest(double principal, double rate, int tenure);
}


SimpleInterestStrategy.java (strategies package)


package com.loan.strategies;

public class SimpleInterestStrategy implements InterestStrategy {

    @Override
    public double calculateInterest(double principal, double rate, int tenure) {
        return (principal * rate * tenure) / 100;
    }
}


LoanEMICalculator.java (main package)


package com.loan.main;

import com.loan.services.LoanService;
import com.loan.services.UserService;
import com.loan.factories.UserFactory;
import com.loan.factories.LoanFactory;
import com.loan.strategies.SimpleInterestStrategy;

public class LoanEMICalculator {

    public static void main(String[] args) {
        UserService userService = UserService.getInstance();
        LoanService loanService = LoanService.getInstance();

        // Create users
        userService.createUser(UserFactory.createUser("admin1", true).getUsername(), true);
        userService.createUser(UserFactory.createUser("customer1", false).getUsername(), false);
        userService.createUser(UserFactory.createUser("customer2", false).getUsername(), false);

        // Create loans by admin
        loanService.createLoan("admin1", "customer1", 10000, 5.0, 2, new SimpleInterestStrategy());
        loanService.createLoan("admin1", "customer1", 5000, 4.0, 1, new SimpleInterestStrategy());

        // Customer makes EMI payment
        loanService.makeEmiPayment("customer1", 500);

        // Fetch loan info for customer
        loanService.fetchLoanInfo("customer1", "customer1");

        // Admin fetches all loans
        loanService.fetchAllLoans("admin1");
    }
}


Explanation of the Solution

  1. Factory Pattern: UserFactory and LoanFactory are used to create instances of User and Loan respectively, ensuring separation of object creation logic.
  2. Singleton Pattern: UserService and LoanService follow the Singleton pattern to ensure only one instance of these services exists, providing centralized management of users and loans.
  3. Strategy Pattern: InterestStrategy is an interface for different interest calculation strategies. SimpleInterestStrategy is a concrete implementation of this strategy. This design allows for easily adding new interest calculation strategies in the future.
  4. Models: User and Loan classes represent the core entities of the system.
  5. Services: UserService and LoanService manage the business logic related to users and loans respectively.
  6. Main Class: LoanEMICalculator is the entry point, demonstrating the creation of users and loans, making EMI payments, and fetching loan information.


Conclusion

This comprehensive example showcases how to design and implement an in-memory loan EMI calculator using key design patterns. By following this approach, you can create a scalable and maintainable codebase, which is crucial for handling complex business logic in real-world applications. Preparing with such examples will help you excel in the machine coding round of interviews for senior software engineering roles at companies like Razorpay.

Comments


Loading...

Post a Comment

Address

Nirvana Apt, Hinjeadi, Pune, Maharastra - 411057 (India)

Website
Site : www.anucodes.in
Social