Case Study Backend Thực Tế

Backend issue thường rơi vào 8 nhóm lớn:

1. Performance
   API chậm, query chậm, response lớn

2. Data Consistency
   Transaction, race condition, duplicate data

3. Security
   Authentication, authorization, validation

4. Reliability
   Timeout, retry, circuit breaker

5. Scalability
   Queue, cache, async processing

6. Integration
   External API, payment, webhook

7. Observability
   Logs, metrics, tracing

8. Maintainability
   Code structure, service layer, testing

1. API bị chậm khi dữ liệu lớn

Bối cảnh

API danh sách order/user/product ban đầu chạy ổn. Sau vài tháng dữ liệu tăng lên nhiều, API bắt đầu chậm hoặc timeout.

Frontend
   │
   │ GET /orders
   ▼
Backend API
   │
   │ SELECT * FROM orders
   ▼
Database
   │
   │ trả về quá nhiều rows
   ▼
Backend xử lý/filter/sort
   │
   ▼
Response rất lớn
   │
   ▼
Frontend render chậm

Nguyên nhân

1. Không có pagination
2. Query database chưa tối ưu
3. Thiếu index
4. Backend lấy quá nhiều field không cần thiết
5. Filter/sort xử lý ở backend memory
6. Response quá lớn
7. Frontend render quá nhiều row cùng lúc

Cách giải quyết

1. Thêm pagination
2. Chỉ select field cần thiết
3. Thêm index cho column hay filter/sort
4. Đưa filter/sort xuống database
5. Dùng cache nếu data ít thay đổi
6. Tối ưu response structure

Ví dụ:

-- Không tốt
SELECT * FROM orders;

-- Tốt hơn
SELECT id, customer_name, status, created_at
FROM orders
WHERE status = 'PENDING'
ORDER BY created_at DESC
LIMIT 20 OFFSET 0;

2. API gọi nhiều service quá chậm

Bối cảnh

Một API dashboard cần lấy nhiều loại dữ liệu:

- User info
- Order summary
- Payment status
- Notification count
- Permission
- Recent activities

Nếu backend gọi tuần tự từng service, tổng thời gian sẽ rất lâu.

GET /dashboard
      │
      ▼
Backend
      │
      ├── call User Service       300ms
      │
      ├── call Order Service      500ms
      │
      ├── call Payment Service    700ms
      │
      ├── call Notification       200ms
      │
      └── call Activity Service   600ms

Total = 300 + 500 + 700 + 200 + 600
      = 2300ms

Cách giải quyết

Nếu các service không phụ thuộc nhau, gọi song song.

GET /dashboard
      │
      ▼
Backend
      │
      ├── User Service
      ├── Order Service
      ├── Payment Service
      ├── Notification Service
      └── Activity Service

Total time ≈ service chậm nhất

Ví dụ Node.js:

const [user, orders, payment, notifications, activities] = await Promise.all([
  getUser(userId),
  getOrderSummary(userId),
  getPaymentStatus(userId),
  getNotificationCount(userId),
  getRecentActivities(userId),
]);

Lưu ý

Promise.all fail nếu một service fail. Dùng Promise.allSettled để xử lý từng kết quả:

const results = await Promise.allSettled([
  getUser(userId),
  getOrderSummary(userId),
  getPaymentStatus(userId),
  getNotificationCount(userId),
  getRecentActivities(userId),
]);

Nên chia data thành:

Critical data
  ├── User info
  └── Permission

Non-critical data
  ├── Notification
  ├── Recent activities
  └── Recommendation

Critical data fail thì trả lỗi. Non-critical data fail thì trả fallback/default value.


3. User bấm submit nhiều lần tạo duplicate data

Bối cảnh

User đặt hàng, tạo payment hoặc submit form. Do mạng chậm, user bấm submit nhiều lần.

Kết quả:

- Tạo 2 orders giống nhau
- Trừ tiền 2 lần
- Gửi email 2 lần
- Insert duplicate record

Diagram vấn đề

User click Submit
   │
   ├── Request 1 ─────► Backend ─────► Create Order
   │
   └── Request 2 ─────► Backend ─────► Create Order again

Frontend disable button là cần thiết, nhưng không đủ.

Cách giải quyết: Idempotency Key

Client gửi một key duy nhất cho mỗi action.

POST /orders
Header: Idempotency-Key: abc-123

Backend xử lý:

Nếu key chưa tồn tại
→ xử lý request
→ lưu result với key này

Nếu key đã tồn tại
→ không xử lý lại
→ trả về result cũ

Diagram:

Client
  │
  │ POST /orders
  │ Idempotency-Key: abc-123
  ▼
Backend
  │
  ├── Check key exists?
  │
  ├── No
  │    ├── Create order
  │    ├── Save result with key
  │    └── Return success
  │
  └── Yes
       └── Return previous result

Kết hợp thêm:

Idempotency key
+ Unique constraint
+ Transaction

Ví dụ:

CREATE UNIQUE INDEX unique_order_request
ON orders(user_id, request_id);

4. Race condition khi nhiều request update cùng một data

Bối cảnh

Có hệ thống booking vé, booking phòng hoặc update stock sản phẩm.

Ví dụ còn 1 sản phẩm trong kho:

User A mua
User B cũng mua cùng lúc

Nếu xử lý không cẩn thận, cả hai đều mua thành công.

Diagram vấn đề

Stock hiện tại = 1

User A request
   │
   ├── Check stock = 1
   └── OK, chuẩn bị update

User B request
   │
   ├── Check stock = 1
   └── OK, chuẩn bị update

Kết quả:
User A success
User B success

Stock bị sai

Cách giải quyết

1. Database transaction
2. Row-level locking
3. Optimistic locking
4. Atomic update
5. Queue xử lý tuần tự

Cách đơn giản: Atomic update

Thay vì:

Step 1: SELECT stock
Step 2: IF stock > 0
Step 3: UPDATE stock

Dùng:

UPDATE products
SET stock = stock - 1
WHERE id = 123 AND stock > 0;

Sau đó check số row affected:

affected rows = 1
→ mua thành công

affected rows = 0
→ hết hàng

Diagram:

Request mua hàng
   │
   ▼
Atomic update:
UPDATE stock = stock - 1
WHERE stock > 0
   │
   ├── affected rows = 1
   │      └── success
   │
   └── affected rows = 0
          └── out of stock

Optimistic locking

Thêm field version:

Product:
id = 123
stock = 1
version = 5

Khi update:

UPDATE products
SET stock = 0, version = version + 1
WHERE id = 123 AND version = 5;

Nếu version đã thay đổi, update fail.


5. Long-running task làm API timeout

Bối cảnh

User bấm export report. Backend cần:

- Query nhiều data
- Generate Excel/PDF
- Upload file lên S3
- Gửi email

API chạy 1–2 phút rồi timeout.

Diagram vấn đề

Frontend
   │
   │ POST /export-report
   ▼
Backend API
   │
   ├── Query large data
   ├── Generate file
   ├── Upload file
   ├── Send email
   └── Return response

Request giữ quá lâu
→ timeout
→ user không biết task thành công hay thất bại

Cách giải quyết: Background Job

API không xử lý toàn bộ task trong request.

1. API nhận request
2. Tạo job
3. Trả jobId ngay cho frontend
4. Worker xử lý job ở background
5. Frontend polling hoặc dùng WebSocket để lấy status

Diagram:

Frontend
   │
   │ POST /export-report
   ▼
Backend API
   │
   ├── Create job
   ├── Save job status = PENDING
   ├── Push job to Queue
   └── Return jobId
          │
          ▼
Frontend nhận jobId
          │
          │ GET /jobs/:jobId/status
          ▼
Backend trả status

Worker:

Queue
  │
  ▼
Worker
  │
  ├── Query data
  ├── Generate file
  ├── Upload file
  ├── Update job status = COMPLETED
  └── Save download URL

Trạng thái job:

PENDING
  │
  ▼
PROCESSING
  │
  ├── COMPLETED
  └── FAILED

6. N+1 Query Problem

Bối cảnh

API lấy danh sách orders và mỗi order cần lấy thêm customer.

Code ban đầu:

Get 100 orders
For each order:
  query customer

Diagram vấn đề

GET /orders
   │
   ▼
Query orders
   │
   ├── Order 1 → Query customer
   ├── Order 2 → Query customer
   ├── Order 3 → Query customer
   ├── ...
   └── Order 100 → Query customer

Total query = 1 + 100

Đây là N+1 query problem.

Cách giải quyết

Cách 1: Join

SELECT orders.id, orders.total, customers.name
FROM orders
JOIN customers ON orders.customer_id = customers.id;

Cách 2: Batch query

1. Query orders
2. Lấy list customerIds
3. Query customers WHERE id IN (...)
4. Map customer vào order

Diagram:

Query orders
   │
   ▼
Get customerIds
   │
   ▼
Query customers in one query
   │
   ▼
Map result

Cách 3: Dataloader

Hay dùng trong GraphQL.

Nhiều request nhỏ
→ gom lại thành một batch request

7. Authentication & Authorization bị thiết kế chưa rõ

Bối cảnh

User đã login nhưng vẫn truy cập được chức năng không đúng role.

Ví dụ:

Normal user gọi API admin
Backend chỉ check token hợp lệ
Nhưng không check permission

Diagram vấn đề

Request
  │
  ├── Có token không?
  │      └── Có
  │
  └── Cho access

Thiếu bước:
Role/Permission có được phép không?

Cách đúng

Phân biệt rõ:

Authentication
= Bạn là ai?

Authorization
= Bạn được phép làm gì?

Diagram chuẩn:

Request
  │
  ▼
Check Authentication
  │
  ├── Token invalid
  │      └── 401 Unauthorized
  │
  └── Token valid
         │
         ▼
Check Authorization
  │
  ├── No permission
  │      └── 403 Forbidden
  │
  └── Has permission
         │
         ▼
Process request

Role-based access control:

User
  │
  ├── Role: Admin
  ├── Role: Manager
  └── Role: Staff

Permission-based access control:

Permission:
- order:read
- order:create
- order:update
- order:delete

8. Cache gây dữ liệu cũ

Bối cảnh

Dùng Redis cache để tăng tốc API. API nhanh hơn nhưng user update data xong vẫn thấy data cũ.

Diagram vấn đề

GET /product/123
   │
   ▼
Check Redis
   │
   ├── Có cache
   │      └── Return cached data
   │
   └── Không có cache
          ├── Query DB
          ├── Save cache
          └── Return data

Khi update product:

Update DB
   │
   └── Nhưng không clear cache

Kết quả:
API vẫn trả data cũ từ Redis

Cách giải quyết

1. TTL - cache tự hết hạn
2. Cache invalidation - update xong thì xóa cache
3. Write-through cache
4. Versioned cache key

Cách phổ biến:

Read:
GET /product/123
→ check cache
→ nếu miss thì query DB

Update:
PUT /product/123
→ update DB
→ delete cache key product:123

Diagram:

Update product
   │
   ├── Update database
   └── Delete Redis key: product:123

Next GET /product/123
   │
   ├── Cache miss
   ├── Query DB
   ├── Save new cache
   └── Return new data

9. File upload lớn làm server quá tải

Bối cảnh

User upload file Excel, image, video.

Nếu backend nhận file rồi lưu local hoặc xử lý trực tiếp sẽ dễ gặp:

- Server memory tăng cao
- Request timeout
- Upload chậm
- Scale nhiều server thì file local bị mất/không đồng bộ

Diagram vấn đề

Frontend upload file
   │
   ▼
Backend server
   │
   ├── Receive large file
   ├── Store in memory/local disk
   ├── Process file
   └── Return response

Backend bị nặng

Giải pháp: Direct upload to cloud storage

1. Frontend request signed URL
2. Backend tạo signed URL
3. Frontend upload trực tiếp lên S3/GCS/Azure Blob
4. Backend chỉ lưu metadata

Diagram:

Frontend
   │
   │ 1. Request signed URL
   ▼
Backend
   │
   │ 2. Generate signed URL
   ▼
Frontend
   │
   │ 3. Upload file directly
   ▼
S3 / Cloud Storage
   │
   │ 4. Return file URL/key
   ▼
Backend
   │
   │ 5. Save metadata to DB
   ▼
Database

Nếu cần xử lý file:

File uploaded to S3
   │
   ▼
Event trigger
   │
   ▼
Queue
   │
   ▼
Worker
   │
   ├── Validate file
   ├── Parse Excel
   ├── Resize image
   ├── Scan virus
   └── Update status

10. Payment callback bị gọi nhiều lần

Bối cảnh

Payment gateway gọi webhook về backend. Webhook có thể bị retry nhiều lần.

- Webhook bị retry
- Callback đến chậm
- Callback đến nhiều lần
- Callback đến không đúng thứ tự

Diagram vấn đề

Payment Gateway
   │
   ├── Webhook 1: SUCCESS
   ├── Webhook 2: SUCCESS
   └── Webhook 3: SUCCESS
          │
          ▼
Backend xử lý nhiều lần

Cách giải quyết

1. Verify signature của webhook
2. Lưu eventId từ payment gateway
3. Idempotent processing
4. Check trạng thái hiện tại của order
5. Dùng transaction

Diagram:

Webhook received
   │
   ▼
Verify signature
   │
   ├── Invalid
   │      └── Reject
   │
   └── Valid
          │
          ▼
Check eventId processed?
   │
   ├── Yes
   │      └── Return 200, do nothing
   │
   └── No
          │
          ▼
Check order status
   │
   ├── Already PAID
   │      └── Return 200
   │
   └── PENDING
          │
          ▼
Update order to PAID
Save eventId
Send email once

11. Database transaction khi tạo order

Bối cảnh

Khi tạo order, backend cần làm nhiều bước:

1. Create order
2. Create order items
3. Deduct stock
4. Create payment record
5. Update coupon usage

Nếu một bước fail thì dữ liệu có thể bị lệch.

Diagram vấn đề

Create order
   │
   ├── Insert order success
   ├── Insert order items success
   ├── Deduct stock success
   ├── Create payment failed
   └── Data inconsistent

Cách giải quyết: Transaction

Start transaction
   │
   ├── Insert order
   ├── Insert order items
   ├── Deduct stock
   ├── Create payment
   └── Commit

Nếu bất kỳ bước nào fail:
   └── Rollback

Diagram:

BEGIN TRANSACTION
   │
   ├── Step 1 success
   ├── Step 2 success
   ├── Step 3 success
   ├── Step 4 failed
   │
   ▼
ROLLBACK

Database quay về trạng thái ban đầu

12. Microservice gọi nhau bị lỗi dây chuyền

Bối cảnh

Service A gọi Service B, B gọi C. Nếu C chậm hoặc lỗi, toàn bộ flow bị ảnh hưởng.

Diagram vấn đề

Frontend
   │
   ▼
Service A
   │
   ▼
Service B
   │
   ▼
Service C bị chậm
   │
   ▼
Toàn bộ request chậm hoặc timeout

Vấn đề

- Timeout không rõ ràng
- Retry quá nhiều gây overload
- Một service chết kéo theo service khác
- Không có fallback

Cách giải quyết

1. Timeout rõ ràng
2. Retry có giới hạn
3. Circuit breaker
4. Fallback response
5. Queue cho task không cần realtime
6. Monitoring/logging/tracing

Circuit breaker:

Nếu service liên tục fail
→ tạm thời ngưng gọi service đó
→ trả fallback nhanh
→ sau một thời gian thử lại

Diagram:

Service A gọi Service B
   │
   ├── B success
   │      └── normal
   │
   ├── B fail nhiều lần
   │      └── circuit open
   │
   └── Khi circuit open
          └── không gọi B nữa, trả fallback

13. Logging không đủ nên debug production rất khó

Bối cảnh

Production có lỗi:

User báo: Tôi bấm submit nhưng không thành công.

Backend log chỉ có:

Error occurred

Không có context như:

- userId
- requestId
- API endpoint
- payload summary
- error stack
- downstream service error

Diagram vấn đề

Production bug
   │
   ▼
Check logs
   │
   └── "Error occurred"
          │
          ▼
Không đủ thông tin để debug

Cách giải quyết

Dùng structured logging:

{
  "level": "error",
  "requestId": "req-123",
  "userId": "user-456",
  "endpoint": "POST /orders",
  "message": "Failed to create order",
  "errorCode": "PAYMENT_TIMEOUT",
  "durationMs": 2300
}

Diagram logging tốt:

Request vào backend
   │
   ├── Generate requestId
   ├── Log request start
   ├── Log important business step
   ├── Log external API call
   ├── Log error with context
   └── Log request end + duration

14. API thiếu validation gây data lỗi

Bối cảnh

Frontend gửi data không hợp lệ nhưng backend vẫn lưu vào DB.

Ví dụ:

{
  "email": "abc",
  "age": -10,
  "quantity": 0
}

Kết quả:

Data sai trong database
Logic phía sau bị lỗi
Report sai

Cách giải quyết

Backend phải validate, không phụ thuộc hoàn toàn vào frontend.

Frontend validation
→ tăng UX

Backend validation
→ bảo vệ data/system

Diagram:

Request
   │
   ▼
Backend validation
   │
   ├── Missing required field
   ├── Wrong data type
   ├── Invalid business rule
   └── Invalid permission
   │
   ▼
Nếu invalid
   └── Return 400 Bad Request

Nếu valid
   └── Process business logic

Validation gồm 2 loại:

Technical validation:
- required
- type
- min/max length
- format email
- enum value

Business validation:
- user có quyền không?
- stock còn không?
- coupon còn hạn không?
- order status có cho cancel không?

Framework phân tích backend issue

┌──────────────────────────────────────────────┐
│              BACKEND ISSUE FRAMEWORK          │
├──────────────────────────────────────────────┤
│ 1. Symptom                                    │
│    User thấy lỗi gì? API chậm? data sai?      │
│                                              │
│ 2. Scope                                      │
│    Ảnh hưởng API nào? user nào? env nào?      │
│                                              │
│ 3. Data Flow                                  │
│    Request đi qua những layer nào?            │
│                                              │
│ 4. Root Cause                                 │
│    Lỗi ở API, DB, cache, queue, external API? │
│                                              │
│ 5. Fix                                        │
│    Fix ngắn hạn và dài hạn là gì?             │
│                                              │
│ 6. Regression Risk                            │
│    Có ảnh hưởng flow cũ không?                │
│                                              │
│ 7. Prevention                                 │
│    Thêm test, log, alert, validation?         │
└──────────────────────────────────────────────┘

Checklist backend production-ready

1. API Design
   - RESTful?
   - status code đúng?
   - response structure rõ?
   - error message consistent?

2. Validation
   - validate input
   - validate business rule
   - validate permission

3. Database
   - index
   - transaction
   - query optimization
   - migration

4. Security
   - authentication
   - authorization
   - rate limit
   - input sanitization
   - secret management

5. Performance
   - pagination
   - cache
   - async processing
   - avoid N+1 query

6. Reliability
   - retry
   - timeout
   - circuit breaker
   - idempotency

7. Observability
   - logs
   - metrics
   - tracing
   - alerting

8. Testing
   - unit test
   - integration test
   - contract test
   - E2E test

Mental model dễ nhớ

Backend tốt =
  đúng logic
+ nhanh
+ an toàn
+ dễ debug
+ chịu tải tốt
+ ít gây data lỗi
+ dễ mở rộng

Flow chuẩn mỗi request:

Request vào backend
   │
   ▼
Validate
   │
   ▼
Authenticate
   │
   ▼
Authorize
   │
   ▼
Process business logic
   │
   ▼
Read/write database
   │
   ▼
Call external services nếu cần
   │
   ▼
Return response
   │
   ▼
Log / monitor / handle error