1. Vì sao đây là bài toán khó?

Khi lượng đơn hàng tăng cao, đặc biệt trong các đợt flash sale, hệ thống thường gặp vấn đề:
  • Oversell (bán vượt số lượng) — có 10 cái hàng nhưng lại bán ra 12 cái
  • Race condition khi nhiều người cùng đặt 1 sản phẩm
  • Kho nhiều địa điểm khiến việc đồng bộ số lượng phức tạp
  • Hủy đơn, hoàn đơn làm số tồn tăng giảm liên tục
Nếu không thiết kế kỹ, tồn kho sẽ trở thành “điểm nghẽn” khiến cả hệ thống sập dây chuyền.

2. Tổng quan kiến trúc inventory hiện đại

[Order Service]  →  [Inventory Service]  →  [Warehouse DB]
         ↕ Event Bus (:contentReference[oaicite:0]{index=0} / :contentReference[oaicite:1]{index=1})
         ↓
 [Inventory Reservation Service]
         ↓
 [Stock Release Scheduler / DLQ]
  • Tách Inventory thành service riêng để có thể scale độc lập.
  • Mọi thao tác đặt hàng, hủy đơn, trả hàng đều thông qua Inventory Service.
  • Giao tiếp qua event-driven để tránh nghẽn cổ chai.

3. Các khái niệm quan trọng

Available stock (số lượng khả dụng)
Số lượng có thể bán = on_hand - reserved
on_hand: tổng tồn kho thực tế trong kho
reserved: số lượng đã được giữ cho đơn hàng chưa thanh toán
Stock reservation (giữ hàng tạm thời)
Khi khách xác nhận đơn → hệ thống reserve số lượng, giảm available ngay
Nếu thanh toán thất bại hoặc hết thời gian giữ → release lại vào kho
Giúp tránh oversell khi nhiều người cùng mua 1 sản phẩm
Multi-warehouse
Mỗi kho có warehouse_id, stock_level riêng
Có thể chia đơn theo kho gần nhất hoặc gom về kho trung tâm
Tính toán shipping cost, thời gian giao dựa vào vị trí kho

4. Thiết kế mô hình dữ liệu

Bảng inventory_stock
| product_id | warehouse_id | on_hand | reserved | updated_at |
Bảng stock_reservation
| reservation_id | product_id | quantity | expires_at | order_id | status |
Bảng inventory_event
| event_id | product_id | type (increase/decrease/reserve/release) | quantity | source (order/return/manual) |
Lưu log sự kiện để audit, rollback khi cần.

5. Luồng xử lý cơ bản

Khi tạo đơn hàng
Gửi request reserveStock(product_id, quantity)
Inventory Service kiểm tra available và giảm reserved
Nếu thành công → cho phép thanh toán
Nếu thất bại → báo hết hàng
Khi thanh toán thành công
Gửi event confirmStock, Inventory giảm on_hand và giảm reserved tương ứng
Khi thanh toán thất bại / hết thời gian giữ
Gửi event releaseStock, Inventory tăng available lại
Dùng scheduler hoặc DLQ để giải phóng reservation hết hạn

6. Kỹ thuật quan trọng để tránh oversell

Atomic update (UPDATE ... WHERE on_hand - reserved >= ?) để kiểm tra và giảm stock cùng lúc
Row-level locking hoặc optimistic locking (version field)
Idempotency key cho API giảm tồn kho → tránh double request
Eventual consistency + compensation logic để rollback khi lỗi

7. Tối ưu hiệu năng

Dùng Redis cache số lượng khả dụng để đọc nhanh
CQRS pattern: đọc số tồn từ cache/search DB, ghi vào transactional DB
Sharding bảng inventory_stock nếu có hàng triệu sản phẩm

8. Kết luận

Hệ thống tồn kho phân tán & chống oversell là xương sống của e-commerce quy mô lớn:
  • Tách thành service riêng, event-driven
  • Sử dụng cơ chế reservation để giữ hàng an toàn
  • Thiết kế dữ liệu chuẩn và cơ chế rollback rõ ràng
Gợi ý: Luôn ưu tiên tính đúng đắn dữ liệu hơn tốc độ, vì lỗi tồn kho ảnh hưởng trực tiếp đến trải nghiệm khách hàng và uy tín thương hiệu.