PhonePe Interview Experience: PhonePe | SDE 2 (3-5 Years) | Bangalore | Aug 2023 [Reject]
Following problem statement was given in Google Meet link and was shared and we had to code LLD in 1hr after which we had to submit the code. Could be done in online protal link or an IDE as well.
Design and implement an Order Management System for Flipkart. You have the following aspects to manage via this system.
Inventory: This is the item store available to sell and also has the price per unit. There can be 2 types of inventory:
Order: When a customer buys something on Flipkart an order is created. It should have basic properties and states that are needed according to the use cases mentioned in the problem.
Mandatory Implementations:
addItemToInventory(itemId, quantity): This is used to add an item or add more quantities of an existing item in Flipkart’s internal inventory. You cannot manage an external seller’s inventory.getAvailableInventory(itemId, seller): This is used to get available inventory for an item and a seller.createOrder(customerId, itemsInfo, address etc): This is used to create an order in the system, Customer can purchase multiple items in one order. Creating an order also provisionally reserves the inventory until it is confirmed. All the items in an order will be from the same seller. This api also tells the total amount that the customer needs to pay. This should also make necessary validations and throw errors.updateOrder(orderId, OrderState): This is used to update an order to confirm, cancel or mark an order fulfilled.
For simplicity we will assume that there is only 1 external seller registered on your platform and it has exposed apis to get/reserve/block inventory
Optional:
Have a mechanism in place to automatically cancel an order and free up the provisional inventory reserved if the order is not confirmed within a particular time duration after creation.
Points to note
How will you be evaluated?
[execution time limit] 3 seconds (java)
[memory limit] 1 GBI worked on IntelliJ IDE to write code and had some minor improvements while having discussions. Adding the final code for reference and also adding the screenshot of package structure I used.
Class Diargam for services


public interface InventoryManager {
void addItemToInventory(int itemId, int quantity);
int getAvailableInventory(int itemId);
void unblockInventoryItems(Map<Integer, Integer> itemIds);
void blockInventoryItems(Map<Integer, Integer> itemIds);
void freeInventoryItems(Map<Integer, Integer> itemIds);
}
public interface OrderManager {
Order createOrder(Integer customerId, Map<Item, Integer> itemsInfo, Address address);
boolean updateOrder(Integer orderId, OrderStatus orderStatus);
}public abstract class InternalInventoryManager implements InventoryManager {
}
public abstract class ExternalInventoryManager implements InventoryManager {
}
public class FlipkartInventoryManager extends InternalInventoryManager {
Map<Integer, Item> inventoryIndex;
public FlipkartInventoryManager() {
inventoryIndex = new HashMap<>();
}
@Override
public void addItemToInventory(int itemId, int quantity) {
if (!ItemProvider.getItemsContext().containsKey(itemId)) {
// Throw item not found error
return;
}
if (inventoryIndex.containsKey(itemId)) {
inventoryIndex.get(itemId).incrementAvailableItemCount(quantity);
} else {
Item item = ItemProvider.getItemsContext().get(itemId);
item.setAvailableItemCount(quantity);
inventoryIndex.put(itemId, item);
}
}
@Override
public int getAvailableInventory(int itemId) {
if (!inventoryIndex.containsKey(itemId)) {
return 0;
}
return inventoryIndex.get(itemId).getAvailableItemCount();
}
@Override
public void unblockInventoryItems(Map<Integer, Integer> itemIds) {
itemIds.forEach((itemId, count) -> inventoryIndex.get(itemId).unblockItem(count));
}
@Override
public void blockInventoryItems(Map<Integer, Integer> itemIds) {
itemIds.forEach((itemId, count) -> inventoryIndex.get(itemId).blockItem(count));
}
@Override
public void freeInventoryItems(Map<Integer, Integer> itemIds) {
itemIds.forEach((itemId, count) -> inventoryIndex.get(itemId).freeItem(count));
}
}
public class CoolCompanyInventoryManager extends ExternalInventoryManager {
@Override
public void addItemToInventory(int itemId, int quantity) {
}
@Override
public int getAvailableInventory(int itemId) {
// Call GetInventory for external API
return 0;
}
@Override
public void unblockInventoryItems(Map<Integer, Integer> itemIds) {
}
@Override
public void blockInventoryItems(Map<Integer, Integer> itemIds) {
// Call BlockInventory for external API
}
@Override
public void freeInventoryItems(Map<Integer, Integer> itemIds) {
}
}public class FlipkartOrdering implements OrderManager {
private final InventoryManager inventoryManager;
private final Map<Integer, Order> orderIndex;
public FlipkartOrdering(InventoryManager inventoryManager) {
this.inventoryManager = inventoryManager;
orderIndex = new HashMap<>();
}
@Override
public Order createOrder(Integer customerId, Map<Item, Integer> itemsInfo, Address address) {
for (Map.Entry<Item, Integer> entry : itemsInfo.entrySet()) {
Item item = entry.getKey();
int quantity = entry.getValue();
int availableItemCount = inventoryManager.getAvailableInventory(item.getItemId());
if (availableItemCount < quantity) {
// Throw items not in stock error for Item: item.getItemID()
return null;
}
}
Order order = new Order(customerId);
Map<Integer, Integer> blockItemIdList = new HashMap<>();
for (Map.Entry<Item, Integer> entry : itemsInfo.entrySet()) {
Item item = entry.getKey();
int quantity = entry.getValue();
order.addItem(item, quantity);
blockItemIdList.put(item.getItemId(), quantity);
}
inventoryManager.blockInventoryItems(blockItemIdList);
orderIndex.put(order.getOrderId(), order);
// This part was added after the interview just as demonstration of one of the ways
// that we can achieve the optional requirement of automatic order cancellation
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.schedule(() -> {
if (order.getOrderStatus() == OrderStatus.CREATED) {
cancelOrder(order);
}
System.out.println(order);
}, 3, TimeUnit.SECONDS);
scheduledExecutorService.shutdown();
return order;
}
@Override
public boolean updateOrder(Integer orderId, OrderStatus orderStatus) {
if (!orderIndex.containsKey(orderId)) {
// Throw order not found
return false;
}
Order order = orderIndex.get(orderId);
if (order.getOrderStatus() == OrderStatus.CANCELLED) {
return false;
}
switch (orderStatus) {
case CANCELLED:
cancelOrder(order);
return true;
case FULFILLED:
order.setOrderStatus(OrderStatus.FULFILLED);
freeInventoryForOrder(order);
return true;
case CONFIRMED:
order.setOrderStatus(OrderStatus.CONFIRMED);
return true;
}
return false;
}
private void freeInventoryForOrder(Order order) {
Map<Integer, Integer> freeItemIdList = new HashMap<>();
order.getItems().forEach((item, count) -> freeItemIdList.put(item.getItemId(), count));
inventoryManager.freeInventoryItems(freeItemIdList);
}
private void unblockInventoryForOrder(Order order) {
Map<Integer, Integer> unblockItemIdList = new HashMap<>();
order.getItems().forEach((item, count) -> unblockItemIdList.put(item.getItemId(), count));
inventoryManager.unblockInventoryItems(unblockItemIdList);
}
private void cancelOrder(Order order) {
order.setOrderStatus(OrderStatus.CANCELLED);
unblockInventoryForOrder(order);
}
}public class ItemProvider {
private static final Map<Integer, Item> items = new HashMap<>();
public static Map<Integer, Item> getItemsContext() {
if (items.isEmpty()) {
mockItemData();
}
return items;
}
private static void mockItemData() {
Item item;
item = new Item("Camera", 100);
items.put(item.getItemId(), item);
item = new Item("Bottle", 20);
items.put(item.getItemId(), item);
item = new Item("Book", 10);
items.put(item.getItemId(), item);
item = new Item("Drone", 250);
items.put(item.getItemId(), item);
}
}
public class InventoryProvider {
public static InventoryManager getInventory(Seller seller) throws SellerNotFoundException {
switch (seller) {
case FLIPKART:
return new FlipkartInventoryManager();
case EXTERNAL_COOL_COMPANY:
return new CoolCompanyInventoryManager();
}
throw new SellerNotFoundException(seller);
}
}public enum OrderStatus {
CREATED,
CONFIRMED,
CANCELLED,
FULFILLED,
}
public enum Seller {
FLIPKART,
EXTERNAL_COOL_COMPANY,
}
public class Item {
// Preferably a database auto increment key
private static int itemIdIndex = 0;
private Integer itemId;
private String name;
private int availableItemCount;
private double price;
private int blockedItemCount;
public Item(String name, int availableItemCount, double price) {
this.itemId = itemIdIndex++;
this.name = name;
this.availableItemCount = availableItemCount;
this.price = price;
this.blockedItemCount = 0;
}
public Item(String name, double price) {
this(name, 0, price);
}
public static int getItemIdIndex() {
return itemIdIndex;
}
public Integer getItemId() {
return itemId;
}
public String getName() {
return name;
}
public int getAvailableItemCount() {
return availableItemCount;
}
public double getPrice() {
return price;
}
public void setAvailableItemCount(int availableItemCount) {
this.availableItemCount = availableItemCount;
}
public void incrementAvailableItemCount(int additionalCount) {
this.availableItemCount = this.availableItemCount + additionalCount;
}
public void blockItem(int itemCount) {
if(itemCount > availableItemCount) {
// throw items not in stock error
return;
}
this.blockedItemCount += itemCount;
this.availableItemCount -= itemCount;
}
public void unblockItem(int itemCount) {
this.blockedItemCount -= itemCount;
this.blockedItemCount = Math.max(0, this.blockedItemCount);
this.availableItemCount += itemCount;
}
public void freeItem(int itemCount) {
this.blockedItemCount -= itemCount;
this.blockedItemCount = Math.max(0, this.blockedItemCount);
}
@Override
public String toString() {
return "Item{" +
"itemId=" + itemId +
", name='" + name + '\'' +
", availableItemCount=" + availableItemCount +
", price=" + price +
", blockedItemCount=" + blockedItemCount +
'}';
}
}
public class Order {
private static int orderIdIndex = 0;
private int orderId;
private OrderStatus orderStatus;
private Date orderDate;
private int customerId;
private Map<Item, Integer> items;
public Order(int customerId) {
this.orderId = orderIdIndex++;
this.customerId = customerId;
this.orderDate = new Date(System.currentTimeMillis());
this.orderStatus = OrderStatus.CREATED;
this.items = new HashMap<>();
}
public void addItem(Item item, int quantity) {
this.items.put(item, quantity);
}
public void setOrderStatus(OrderStatus orderStatus) {
this.orderStatus = orderStatus;
}
public static int getOrderIdIndex() {
return orderIdIndex;
}
public int getOrderId() {
return orderId;
}
public OrderStatus getOrderStatus() {
return orderStatus;
}
public Date getOrderDate() {
return orderDate;
}
public int getCustomerId() {
return customerId;
}
public Map<Item, Integer> getItems() {
return items;
}
@Override
public String toString() {
return "Order{" +
"orderId=" + orderId +
", orderStatus=" + orderStatus +
", orderDate=" + orderDate +
", customerId=" + customerId +
", items=" + items +
'}';
}
}
public class Address {
private String address;
private int zipCode;
private String state;
private String country;
public Address(String address, int zipCode, String state, String country) {
this.address = address;
this.zipCode = zipCode;
this.state = state;
this.country = country;
}
public String getAddress() {
return address;
}
public int getZipCode() {
return zipCode;
}
public String getState() {
return state;
}
public String getCountry() {
return country;
}
}public class Solution {
public static void main(String[] args) throws Exception {
InventoryManager inventoryManager = InventoryProvider.getInventory(Seller.FLIPKART);
inventoryManager.addItemToInventory(0, 10);
inventoryManager.addItemToInventory(2, 12);
inventoryManager.addItemToInventory(1, 13);
System.out.println("Available Inventory for [id = 0]: " + inventoryManager.getAvailableInventory(0));
System.out.println("Available Inventory for [id = 1]: " + inventoryManager.getAvailableInventory(1));
OrderManager orderManager = new FlipkartOrdering(inventoryManager);
int customerID = 1;
Map<Item, Integer> itemsInfo = new HashMap<>();
itemsInfo.put(ItemProvider.getItemsContext().get(0), 9);
Address address = new Address("", 100000, "Bangalore", "India");
Order order = orderManager.createOrder(customerID, itemsInfo, address);
System.out.println(order);
TimeUnit.SECONDS.sleep(4);
if (orderManager.updateOrder(order.getOrderId(), OrderStatus.CONFIRMED)) {
System.out.println(order);
// We can see blocked and freed items in LOGS
orderManager.updateOrder(order.getOrderId(), OrderStatus.FULFILLED);
System.out.println(order);
} else {
System.out.println("Order already cancelled");
}
}
}