Shivam Chauhan
about 1 month 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