# PART 2 — biến thể `api-service` > **Dùng khi** `PRODUCT = api-service` — sản phẩm không có giao diện; người tiêu thụ là hệ > thống khác hoặc team khác. Cắm khối này vào chỗ PART 2 của [`../srs.md`](../srs.md). > > **Tiêu chí G3 riêng của biến thể này**: mỗi endpoint có bảng tham số/schema đầy đủ · mọi > mã lỗi map về endpoint · đã trả lời xong ba câu idempotency / tương thích ngược / phân > trang · có ít nhất một team tiêu thụ đã đọc và xác nhận. 🔴 **Với loại này, `API_.md` là artifact chính, không phải phụ lục.** PART 2 ở đây mô tả *hợp đồng nhìn từ phía người tiêu thụ*; chi tiết kỹ thuật từng endpoint vẫn ở [`../api-contract.md`](../api-contract.md). Đừng chép trùng — PART 2 trả lời "có những khả năng gì", `API` trả lời "gọi thế nào". --- ## 2.1 Người tiêu thụ *Thay cho "danh sách màn hình". Ai gọi API này quyết định AC viết thế nào.* | ID | Người tiêu thụ | Là ai | Gọi để làm gì | Tần suất dự kiến | Đầu mối | |---|---|---|---|---|---| | CON-01 | | Hệ thống nội bộ / Đối tác ngoài / App di động | | | | **Ba câu bắt buộc:** | Câu hỏi | Trả lời | |---|---| | Có người tiêu thụ nào **ngoài tổ chức** không? | *(quyết định mức chặt của versioning và bảo mật)* | | Người tiêu thụ có tự thử được không, hay cần môi trường sandbox? | | | Ai được thêm người tiêu thụ mới, và bằng quy trình gì? | | ## 2.2 Danh sách khả năng | ID | Khả năng nghiệp vụ | Endpoint | Method | Người tiêu thụ | Đồng bộ/Bất đồng bộ | BR | |---|---|---|---|---|---|---| | CAP-01 | Tra cứu … | `/api/v1/…` | GET | CON-01 | Đồng bộ | | | CAP-02 | Ghi nhận … | `/api/v1/…` | POST | CON-01, CON-02 | Bất đồng bộ (trả 202 + callback) | BR-0nn | ## 2.3 Sơ đồ luồng gọi ```mermaid sequenceDiagram autonumber participant C1 as CON-01 (người gọi) participant SVC as Service participant DB as Cơ sở dữ liệu participant Q as Hàng đợi participant C2 as CON-02 (người tiêu thụ event) C1->>SVC: POST /orders (Idempotency-Key) SVC->>DB: Ghi bản ghi alt Ghi thành công DB-->>SVC: OK SVC->>Q: publish OrderCreated SVC-->>C1: 201 + id Q-->>C2: OrderCreated else Trùng Idempotency-Key DB-->>SVC: đã tồn tại SVC-->>C1: 200 + id cũ (không tạo bản ghi thứ hai) else Lỗi ghi DB--xSVC: lỗi SVC-->>C1: 503 · E-XXX-0503 (retry được) end ``` 🔴 **Bắt buộc vẽ ba nhánh**: thành công · **gọi lại trùng** · lỗi. Nhánh giữa là nhánh chứng minh §2.4.1 idempotency hoạt động — thiếu nó thì người tiêu thụ không biết gọi lại có an toàn không. **Bảng đi kèm** *(quy tắc W13)*: | Bước | Đồng bộ / Bất đồng bộ | Timeout | Retry được | Mã lỗi | |---|---|---|---|---| | 1 | Đồng bộ | 5s | ✅ với `Idempotency-Key` | | | 5 | Bất đồng bộ | — | Hàng đợi tự retry 3 lần | | Với luồng **bất đồng bộ**, bắt buộc trả lời ba câu — sơ đồ không nói được: | Câu hỏi | Trả lời | |---|---| | Người gọi biết kết quả bằng cách nào | polling `GET /orders/{id}` · callback · event | | Chờ tối đa bao lâu | | | Quá hạn mà chưa có kết quả thì làm gì | | --- ## 2.4 CAP-01 — ### 2.4.1 Hợp đồng | | | |---|---| | **Endpoint** | `POST /api/v1/…` | | **Quyền** | scope `…` / client `…` | | **Idempotent** | ✅/❌ — nếu ✅: khoá idempotency là gì, giữ bao lâu | | **Gọi lại an toàn (retry)** | ✅/❌ | | **Thời gian phản hồi mục tiêu** | ≤ … ms (p95) | | **Giới hạn tần suất** | … req/phút/client · vượt thì trả gì | 🔴 **Ba câu này là chỗ hay bỏ sót nhất của API spec:** 1. **Idempotency** — người gọi timeout rồi gọi lại, có tạo hai bản ghi không? Nếu không idempotent thì phải nói rõ để người tiêu thụ tự xử lý. 2. **Retry** — lỗi nào được retry, lỗi nào không? Khuyến nghị backoff bao nhiêu? 3. **Đồng thời** — hai request cùng sửa một tài nguyên thì sao? Có optimistic locking không? ### 2.4.2 Tham số / Request | Tên | Kiểu | Vị trí | Bắt buộc | Mặc định | Ràng buộc | BR | |---|---|---|---|---|---|---| | | string | query / path / body | ✅ | — | 3–20 ký tự, `^[A-Z0-9-]+$` | BR-0nn | *Cột `Ràng buộc` là tương đương của "bảng field" ở biến thể `screen` — phải cụ thể ngang vậy.* ### 2.4.3 Response thành công | Trường | Kiểu | Có thể null | Nghĩa nghiệp vụ | Ghi chú | |---|---|---|---|---| | | | | | | ### 2.4.4 Response lỗi | HTTP | `code` | Khi nào | Người gọi nên làm gì | Mã lỗi SRS §4.1 | |---|---|---|---|---| | 409 | | | Không retry, sửa dữ liệu | `E-XXX-0001` | | 503 | | | Retry với backoff | `E-XXX-0503` | **Cột "Người gọi nên làm gì" là cột thay thế cho "hiển thị ở đâu" của biến thể `screen`.** Không có nó thì mỗi team tiêu thụ tự đoán một kiểu xử lý lỗi. --- ## 2.5 Hợp đồng dữ liệu chung | # | Vấn đề | Quyết định | |---|---|---| | 1 | **Số lớn** (id, số tiền) | `string` / `number` — vượt `2^53` thì JS làm tròn sai | | 2 | **Thời gian** | Định dạng, múi giờ | | 3 | **Phân trang** | offset / cursor · có `total`? có `hasNext`? | | 4 | **Sắp xếp** | Cú pháp, trường nào cho phép | | 5 | **Trường null vs. vắng mặt** | Có khác nghĩa không | | 6 | **Enum** | Người tiêu thụ gặp giá trị lạ (mới thêm) thì xử lý sao | ## 2.6 Phiên bản & tương thích ngược | | | |---|---| | **Cách đánh version** | URL `/v1/` · header · … | | **Thay đổi nào là phá vỡ tương thích** | *(bỏ trường, đổi kiểu, thêm ràng buộc, đổi nghĩa mã lỗi)* | | **Thay đổi nào là an toàn** | *(thêm trường optional, thêm giá trị enum — **chỉ khi** §2.5 #6 đã định nghĩa)* | | **Báo trước bao lâu khi bỏ version cũ** | | | **Chạy song song mấy version** | | 🔴 **Thêm một giá trị enum là thay đổi phá vỡ tương thích** nếu §2.5 #6 không nói người tiêu thụ phải làm gì với giá trị lạ. Đây là lỗi tương thích phổ biến nhất và im lặng nhất. ## 2.7 Trạng thái tương đương "màn hình rỗng" | Tình huống | Trả về gì | HTTP | |---|---|---| | Truy vấn hợp lệ, không có bản ghi nào | Mảng rỗng + paging `total: 0` — **không phải 404** | 200 | | Tài nguyên không tồn tại | | 404 | | Tài nguyên tồn tại nhưng không có quyền | 404 hay 403? *(404 giấu sự tồn tại — chọn theo mức nhạy cảm)* | | ## 2.8 Môi trường & tích hợp thử | | | |---|---| | Sandbox | Có / Không — đường dẫn | | Dữ liệu mẫu cho người tiêu thụ thử | | | Cách cấp credential | | | Tài liệu tích hợp bàn giao ở đâu | *(đây là `MANUAL` của GĐ5 với loại sản phẩm này)* |