Shivam Chauhan
14 days ago
Ever wondered how Amazon, Flipkart, or your favourite online store keeps track of millions of items, ensuring they don't sell something they don't have? It's all about a robust, real-time inventory management system. Today, we're diving deep into the low-level design (LLD) of such a system.
In the fast-paced world of e-commerce, accuracy and speed are key. Imagine this: a customer adds the last available item to their cart, but before they can checkout, someone else snatches it up. A frustrating out-of-stock message appears, leading to a lost sale and a potentially unhappy customer.
A real-time inventory system prevents this by:
So, how do we build such a system? Let's get into the nitty-gritty.
Our real-time inventory system will consist of these key components:
Here’s a basic diagram to illustrate the architecture:
Let's break down each component and its responsibilities.
sqlCREATE TABLE products (
product_id UUID PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
quantity INTEGER NOT NULL
);
APIs:
Implementation: The service should handle authentication, authorisation, and input validation. It should also interact with the cache and the message queue.
java@RestController
@RequestMapping("/inventory")
public class InventoryController {
@Autowired
private InventoryService inventoryService;
@GetMapping("/{productId}")
public ResponseEntity<Integer> getInventory(@PathVariable UUID productId) {
int quantity = inventoryService.getInventory(productId);
return ResponseEntity.ok(quantity);
}
@PostMapping("/{productId}/add")
public ResponseEntity<Void> addInventory(@PathVariable UUID productId, @RequestParam int quantity) {
inventoryService.addInventory(productId, quantity);
return ResponseEntity.ok().build();
}
@PostMapping("/{productId}/subtract")
public ResponseEntity<Void> subtractInventory(@PathVariable UUID productId, @RequestParam int quantity) {
inventoryService.subtractInventory(productId, quantity);
return ResponseEntity.ok().build();
}
}
Purpose: Decouples the inventory updates from the main application flow. When an order is placed or inventory is adjusted, a message is published to the queue.
Configuration: Configure the queue for high availability and durability to prevent message loss.
Example: Publishing a message when an order is placed:
java@Service
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private Queue inventoryUpdateQueue;
public void placeOrder(Order order) {
// ... other order processing logic ...
// Publish message to update inventory
rabbitTemplate.convertAndSend(inventoryUpdateQueue.getName(), new InventoryUpdateMessage(order.getProductId(), order.getQuantity()));
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class InventoryUpdateMessage {
private UUID productId;
private int quantity;
}
Strategy: Use a read-through/write-through cache. When data is requested, the cache is checked first. If the data is not present (cache miss), it's retrieved from the database and stored in the cache.
Invalidation: Implement a mechanism to invalidate the cache when inventory is updated to ensure data consistency.
java@Service
public class InventoryService {
@Autowired
private RedisTemplate<String, Integer> redisTemplate;
@Autowired
private ProductRepository productRepository;
public int getInventory(UUID productId) {
String key = "inventory:" + productId.toString();
Integer quantity = redisTemplate.opsForValue().get(key);
if (quantity == null) {
// Cache miss, fetch from database
Product product = productRepository.findById(productId)
.orElseThrow(() -> new IllegalArgumentException("Product not found"));
quantity = product.getQuantity();
redisTemplate.opsForValue().set(key, quantity);
}
return quantity;
}
public void addInventory(UUID productId, int quantity) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new IllegalArgumentException("Product not found"));
product.setQuantity(product.getQuantity() + quantity);
productRepository.save(product);
// Invalidate cache
String key = "inventory:" + productId.toString();
redisTemplate.delete(key);
}
public void subtractInventory(UUID productId, int quantity) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new IllegalArgumentException("Product not found"));
if (product.getQuantity() < quantity) {
throw new IllegalArgumentException("Insufficient stock");
}
product.setQuantity(product.getQuantity() - quantity);
productRepository.save(product);
// Invalidate cache
String key = "inventory:" + productId.toString();
redisTemplate.delete(key);
}
}
Data consistency is paramount. Here are some strategies:
As your e-commerce platform grows, you'll need to scale your inventory system. Here are some techniques:
1. What database should I use?
2. How do I handle inventory updates during peak hours?
3. How do I monitor the system?
4. How does Coudo AI help in understanding these concepts?
Architecting a real-time inventory management system for e-commerce is a complex but crucial task. By understanding the core components, implementing proper consistency mechanisms, and designing for scalability, you can build a system that meets the demands of your growing business. Remember to practice your skills and explore more problems on platforms like Coudo AI to become a proficient system designer.
Keep it real, keep it fresh, and keep it engaging! And remember, the key to mastering LLD is continuous learning and practice.\n\n