init git
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# 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_<US>.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 — <Tên khả năng>
|
||||
|
||||
### 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)* |
|
||||
@@ -0,0 +1,176 @@
|
||||
# PART 2 — biến thể `batch-job`
|
||||
|
||||
> **Dùng khi** `PRODUCT = batch-job` — job chạy theo lịch, không giao diện: đối soát đêm,
|
||||
> sinh báo cáo định kỳ, dọn dữ liệu, gửi thông báo hàng loạt. 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 job có lịch + cửa sổ + phụ thuộc · quy tắc xử
|
||||
> lý từng bản ghi đầy đủ · **đã trả lời xong idempotency và thất bại giữa chừng** · có kế
|
||||
> hoạch cảnh báo và người trực.
|
||||
|
||||
**Phân biệt với `data-pipeline`:** pipeline có sản phẩm là **dữ liệu để phân tích** (cần
|
||||
data contract, lineage, đối soát nguồn–đích); batch-job có sản phẩm là **việc được làm xong**
|
||||
(cần idempotency, checkpoint, cảnh báo). Job vừa di chuyển dữ liệu lớn vừa làm việc nghiệp
|
||||
vụ ⇒ nạp cả hai biến thể.
|
||||
|
||||
---
|
||||
|
||||
## 2.1 Danh sách job
|
||||
|
||||
| ID | Job | Làm gì | Lịch | Cửa sổ cho phép | Phụ thuộc job nào | Khối lượng/lần |
|
||||
|---|---|---|---|---|---|---|
|
||||
| JOB-01 | | | Hằng ngày 01:00 | 01:00–05:00 | JOB-00 xong | ~… bản ghi |
|
||||
|
||||
**Cột `Cửa sổ cho phép`** — job phải xong trước mấy giờ, và vì sao (nghiệp vụ nào bắt đầu
|
||||
lúc đó). Không có cột này thì không ai biết chạy chậm bao lâu là sự cố.
|
||||
|
||||
## 2.2 Sơ đồ phụ thuộc
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
J00["JOB-00 · 02:00 Nạp dữ liệu"]
|
||||
J01["JOB-01 · 02:30 Đối soát"]
|
||||
J02["JOB-02 · 03:00 Gửi báo cáo"]
|
||||
STOP(["⛔ Dừng chuỗi · cảnh báo trực đêm"])
|
||||
|
||||
J00 -->|xong| J01
|
||||
J01 -->|xong| J02
|
||||
J00 -->|thất bại| STOP
|
||||
J01 -->|thất bại| STOP
|
||||
```
|
||||
|
||||
*Nhãn node mang **giờ chạy**; ID khớp cột `ID` bảng §2.1.*
|
||||
|
||||
🔴 **Vẽ cả cạnh thất bại.** Sơ đồ chỉ có đường "xong" bỏ sót đúng câu hỏi quan trọng nhất:
|
||||
job trước hỏng thì job sau **chạy hay dừng**.
|
||||
|
||||
**Bảng đi kèm** *(quy tắc W13)*:
|
||||
|
||||
| Job | Phụ thuộc | Job trước thất bại ⇒ | Chạy với dữ liệu cũ có nguy hiểm không | Ai được báo |
|
||||
|---|---|---|---|---|
|
||||
| JOB-01 | JOB-00 | Dừng, không chạy | 🔴 Có — đối soát trên dữ liệu thiếu ra kết quả sai | Trực đêm |
|
||||
| JOB-02 | JOB-01 | Dừng, không gửi báo cáo | 🔴 Có — gửi báo cáo sai còn tệ hơn không gửi | Trực đêm + kế toán |
|
||||
|
||||
Cột áp chót là cột quyết định: dữ liệu cũ vô hại ⇒ cho chạy tiếp; nguy hiểm ⇒ phải dừng.
|
||||
Không trả lời được ⇒ `OQ`, đừng mặc định cho chạy.
|
||||
|
||||
---
|
||||
|
||||
## 2.3 JOB-01 — <Tên job>
|
||||
|
||||
### 2.3.1 Định danh
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Kích hoạt bởi** | Lịch / Sự kiện / Gọi tay |
|
||||
| **Đầu vào** | *(bảng, file, hàng đợi — và phạm vi: ngày nào, trạng thái nào)* |
|
||||
| **Đầu ra** | *(bản ghi được cập nhật, file sinh ra, thông báo gửi đi)* |
|
||||
| **Thời gian chạy dự kiến** | ~… phút với khối lượng bình thường |
|
||||
| **Chạy bao lâu thì coi là treo** | |
|
||||
| **Có thể chạy đồng thời nhiều bản không** | 🔴 Không ⇒ cơ chế khoá là gì |
|
||||
|
||||
### 2.3.2 Phạm vi xử lý
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Chọn bản ghi nào để xử lý** | *(điều kiện chính xác)* |
|
||||
| **Bản ghi đã xử lý rồi nhận biết bằng gì** | 🔴 *(cột trạng thái? bảng log? mốc thời gian?)* |
|
||||
| **Thứ tự xử lý có quan trọng không** | |
|
||||
| **Giới hạn số bản ghi mỗi lần chạy** | *(và phần còn lại xử lý khi nào)* |
|
||||
|
||||
### 2.3.3 Quy tắc xử lý từng bản ghi
|
||||
|
||||
*Đây là phần thay thế cho "bảng field" của biến thể `screen` — phải cụ thể ngang vậy.*
|
||||
|
||||
| # | Điều kiện | Hành động | Kết quả ghi vào đâu | BR |
|
||||
|---|---|---|---|---|
|
||||
| 1 | | | | BR-0nn |
|
||||
|
||||
**Bản ghi lỗi giữa chừng:**
|
||||
|
||||
| Tình huống | Xử lý | Ghi log gì |
|
||||
|---|---|---|
|
||||
| Dữ liệu bản ghi không hợp lệ | Bỏ qua và tiếp tục / Dừng cả job | |
|
||||
| Gọi hệ thống ngoài thất bại | Retry mấy lần? Rồi sao? | |
|
||||
|
||||
🔴 **"Bỏ qua và tiếp tục" phải kèm ngưỡng.** Bỏ qua 3 bản ghi là bình thường; bỏ qua 30% số
|
||||
bản ghi là sự cố nhưng job vẫn báo "thành công" — đó là chế độ hỏng im lặng.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Ngưỡng tỷ lệ lỗi để job tự đánh dấu thất bại** | > …% |
|
||||
|
||||
### 2.3.4 Idempotency — chạy lại
|
||||
|
||||
🔴 **Câu hỏi bắt buộc, không được để trống:**
|
||||
|
||||
| Câu hỏi | Trả lời |
|
||||
|---|---|
|
||||
| Chạy lại cùng một ngày hai lần ⇒ kết quả có giống lần đầu không? | ✅/❌ |
|
||||
| Nếu ❌: hậu quả cụ thể là gì | *(gửi email hai lần? cộng tiền hai lần?)* |
|
||||
| Nếu ❌: quy trình dọn trước khi chạy lại | |
|
||||
| Ai được phép chạy lại | |
|
||||
| Chạy lại có cần khoảng thời gian cụ thể không | |
|
||||
|
||||
Job **gửi thông báo, ghi bút toán, gọi API bên ngoài** mà không idempotent là rủi ro nghiêm
|
||||
trọng — nêu rõ trong `RISK` chứ không chỉ ghi ở đây.
|
||||
|
||||
### 2.3.5 Thất bại giữa chừng
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Có checkpoint không** | ✅/❌ — lưu ở đâu, mức nào (mỗi bản ghi? mỗi lô?) |
|
||||
| **Chạy lại tiếp tục từ checkpoint hay từ đầu** | |
|
||||
| **Có rollback không** | Toàn bộ / Không có / Từng lô |
|
||||
| **Trạng thái dở dang có làm hỏng nghiệp vụ khác không** | 🔴 *(job sau đọc dữ liệu chưa xong)* |
|
||||
| **Cách nhận biết job đang chạy dở vs. đã xong** | |
|
||||
|
||||
### 2.3.6 Chạy tay
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Ai được chạy tay | |
|
||||
| Tham số truyền được | *(ngày nào, phạm vi nào)* |
|
||||
| Có cần phê duyệt không | *(bắt buộc nếu `RIGOR = strict`)* |
|
||||
| Chạy tay có ghi vết khác chạy tự động không | |
|
||||
|
||||
## 2.4 Trạng thái tương đương "màn hình rỗng"
|
||||
|
||||
| Tình huống | Job làm gì | Coi là thành công? |
|
||||
|---|---|---|
|
||||
| Không có bản ghi nào thoả điều kiện | Kết thúc, ghi log "0 bản ghi" | ✅ Có — **không phải lỗi** |
|
||||
| Đầu vào chưa sẵn sàng (job trước chưa xong) | Chờ / Bỏ qua lần này / Báo lỗi | |
|
||||
| Chạy đúng lịch nhưng hôm đó nghỉ lễ | | |
|
||||
|
||||
🔴 Phân biệt **"không có gì để làm"** với **"không lấy được dữ liệu"**. Cả hai đều ra 0 bản
|
||||
ghi nhưng ý nghĩa ngược nhau, và gộp chúng làm sự cố bị bỏ qua nhiều ngày.
|
||||
|
||||
## 2.5 Cảnh báo và vận hành
|
||||
|
||||
| Sự kiện | Mức | Báo cho ai | Qua kênh nào | Trong bao lâu |
|
||||
|---|---|---|---|---|
|
||||
| Job thất bại | 🔴 | | | Ngay |
|
||||
| Job chạy quá cửa sổ cho phép | 🔴 | | | Ngay |
|
||||
| Tỷ lệ bản ghi lỗi vượt ngưỡng | 🟠 | | | Ngay |
|
||||
| **Job không chạy** *(lịch không kích hoạt)* | 🔴 | | | Sau … phút quá giờ |
|
||||
|
||||
🔴 **Dòng cuối là dòng hay bị quên nhất.** Job thất bại thì có cảnh báo; job *không chạy*
|
||||
thì im lặng hoàn toàn — không có gì để báo lỗi. Phải có cơ chế phát hiện "đến giờ mà chưa
|
||||
thấy job nào bắt đầu".
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Ai trực ban đêm** | |
|
||||
| **Sổ tay xử lý sự cố ở đâu** | *(đây là `MANUAL` của GĐ5 với loại sản phẩm này)* |
|
||||
| **Hỏng bao lâu thì phải báo người dùng nghiệp vụ** | |
|
||||
|
||||
## 2.6 Nghiệm thu — thay cho UAT thông thường
|
||||
|
||||
| # | Cách kiểm | Tiêu chí đi tiếp |
|
||||
|---|---|---|
|
||||
| 1 | Chạy trên dữ liệu sao chép từ môi trường thật | Kết quả khớp với xử lý tay trên mẫu … bản ghi |
|
||||
| 2 | **Chạy song song với cách làm cũ** ≥ 1 chu kỳ nghiệp vụ | Sai lệch = 0, hoặc mọi sai lệch giải thích được |
|
||||
| 3 | Thử chạy lại | Kết quả không đổi (nếu idempotent) |
|
||||
| 4 | Thử ngắt giữa chừng rồi chạy lại | Không mất, không trùng bản ghi |
|
||||
|
||||
Bước 4 hay bị bỏ, và nó là bước duy nhất chứng minh §2.3.5 hoạt động thật.
|
||||
@@ -0,0 +1,164 @@
|
||||
# PART 2 — biến thể `data-pipeline`
|
||||
|
||||
> **Dùng khi** `PRODUCT = data-pipeline` — sản phẩm là **dữ liệu**: ETL, ingest, kho dữ
|
||||
> liệu, báo cáo BI. 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 luồng có data contract nguồn và đích · bảng
|
||||
> ánh xạ trường đầy đủ · quy tắc chất lượng có ngưỡng và hành vi khi vi phạm · **có mục đối
|
||||
> soát nguồn–đích** · đã trả lời xong chạy lại/backfill/dữ liệu đến muộn.
|
||||
|
||||
🔴 **Người dùng của bạn không nhìn thấy sản phẩm — họ nhìn thấy những con số.** Con số sai
|
||||
mà không ai biết là chế độ hỏng nguy hiểm nhất của loại sản phẩm này, vì nó im lặng. Vì vậy
|
||||
§2.6 (đối soát) và §2.5 (chất lượng) là hai mục quan trọng nhất, không phải phần phụ.
|
||||
|
||||
---
|
||||
|
||||
## 2.1 Danh sách luồng dữ liệu
|
||||
|
||||
| ID | Luồng | Nguồn | Đích | Tần suất | Kiểu | Khối lượng/lần | BR |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| FLW-01 | | | | Hằng đêm 02:00 | Toàn bộ / Tăng dần / CDC | ~… bản ghi | |
|
||||
|
||||
## 2.2 Sơ đồ lineage
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
POS[["POS API"]]
|
||||
ERP[["ERP export CSV"]]
|
||||
STG[("staging.pos_raw")]
|
||||
DW[("dw.fact_sales")]
|
||||
RPT(["Báo cáo doanh thu"])
|
||||
NGUOIDOC(["👤 Kế toán, Ban giám đốc"])
|
||||
|
||||
POS -->|FLW-01| STG
|
||||
STG -->|FLW-02| DW
|
||||
ERP -->|FLW-03| DW
|
||||
DW --> RPT
|
||||
RPT --> NGUOIDOC
|
||||
```
|
||||
|
||||
*Trụ `[( )]` = kho dữ liệu · khung đôi `[[ ]]` = nguồn ngoài, không do mình sở hữu · bo tròn
|
||||
`([ ])` = đầu ra và người đọc. Nhãn cạnh là mã luồng `FLW-nn`, khớp bảng §2.1.*
|
||||
|
||||
🔴 **Vẽ tới tận người tiêu thụ cuối** — báo cáo nào, **ai đọc**. Dừng ở bảng dữ liệu thì khi
|
||||
luồng hỏng lúc 2 giờ sáng không ai biết phải báo cho ai, và không đánh giá được mức nghiêm
|
||||
trọng.
|
||||
|
||||
**Bảng đi kèm** *(quy tắc W13)*:
|
||||
|
||||
| Luồng | Nguồn → Đích | Tần suất | SLA độ tươi | Hỏng thì ai bị ảnh hưởng | Chủ sở hữu nguồn |
|
||||
|---|---|---|---|---|---|
|
||||
| FLW-01 | POS API → staging | Hằng đêm 02:00 | D+1 08:00 | Toàn bộ chuỗi phía sau | NCC X — anh Huy |
|
||||
|
||||
## 2.3 FLW-01 — <Tên luồng>
|
||||
|
||||
### 2.3.1 Nguồn
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Hệ thống nguồn** | |
|
||||
| **Cách lấy** | API / file / CDC / queue |
|
||||
| **Ai sở hữu nguồn** | *(đầu mối khi schema đổi)* |
|
||||
| **Nguồn có báo trước khi đổi schema không** | 🔴 Không ⇒ phải có phát hiện schema drift |
|
||||
| **Cửa sổ dữ liệu sẵn sàng** | *(từ mấy giờ nguồn mới có đủ dữ liệu hôm qua)* |
|
||||
|
||||
### 2.3.2 SLA độ tươi
|
||||
|
||||
*Thay cho "thời gian phản hồi" của biến thể `screen`.*
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Dữ liệu ngày D phải sẵn sàng trước** | D+1 08:00 |
|
||||
| **Trễ tối đa chấp nhận được** | |
|
||||
| **Ai được báo khi trễ** | |
|
||||
| **Người dùng thấy gì khi dữ liệu chưa tới** | 🔴 Số cũ? Số rỗng? **Có nhãn cảnh báo không?** |
|
||||
|
||||
🔴 Câu cuối là câu hay bị bỏ nhất. Báo cáo hiển thị số của hôm kia mà không có nhãn "dữ liệu
|
||||
tới 28/08" là cách người dùng ra quyết định trên số cũ mà không biết.
|
||||
|
||||
### 2.3.3 Data contract — schema nguồn
|
||||
|
||||
| Trường nguồn | Kiểu | Có thể null | Nghĩa nghiệp vụ | Giá trị hợp lệ | Ghi chú |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
### 2.3.4 Ánh xạ trường
|
||||
|
||||
| Trường đích | Từ trường nguồn | Phép biến đổi | Khi nguồn null | Khi nguồn sai định dạng | BR |
|
||||
|---|---|---|---|---|---|
|
||||
| `store_code` | `shop.id` | upper(trim(x)) | → `UNKNOWN` | → quarantine | BR-0nn |
|
||||
| `amount` | `total` | chia 100 (nguồn lưu đơn vị nhỏ nhất) | → 0 | → quarantine | BR-0nn |
|
||||
|
||||
🔴 **Ba cột cuối là phần thay thế cho "validation + message lỗi" của biến thể `screen`.**
|
||||
Bỏ trống ⇒ kỹ sư dữ liệu tự quyết, và mỗi luồng một kiểu.
|
||||
|
||||
### 2.3.5 Khoá và trùng lặp
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Khoá nghiệp vụ** | *(cái gì xác định một bản ghi là duy nhất)* |
|
||||
| **Nguồn có gửi trùng không** | |
|
||||
| **Trùng thì xử lý sao** | Giữ bản mới nhất / cộng dồn / báo lỗi |
|
||||
| **Bản ghi bị sửa ở nguồn** | Ghi đè / giữ lịch sử (SCD loại mấy) |
|
||||
|
||||
## 2.4 Quy tắc chất lượng dữ liệu
|
||||
|
||||
| ID | Kiểm tra gì | Ngưỡng | Vi phạm thì làm gì | Ai được báo |
|
||||
|---|---|---|---|---|
|
||||
| DQ-01 | Số bản ghi so với trung bình 7 ngày | ±30% | ⚠️ Cảnh báo, vẫn nạp | |
|
||||
| DQ-02 | Tỷ lệ `store_code` không map được | > 1% | 🛑 **Dừng luồng** | |
|
||||
| DQ-03 | Tổng tiền âm | > 0 bản ghi | 🔴 Quarantine bản ghi đó | |
|
||||
|
||||
**Ba hành vi khi vi phạm — chọn rõ một, không được để mơ hồ:**
|
||||
|
||||
| Hành vi | Nghĩa | Dùng khi |
|
||||
|---|---|---|
|
||||
| `drop` | Bỏ bản ghi, ghi log | Bản ghi rác đã biết, không ảnh hưởng tổng |
|
||||
| `quarantine` | Tách sang bảng riêng để xử lý tay | 🔴 **Mặc định nên chọn** — giữ được dữ liệu để điều tra |
|
||||
| `fail` | Dừng cả luồng | Sai lệch có thể làm hỏng báo cáo tài chính |
|
||||
|
||||
🔴 **`drop` im lặng là chế độ hỏng tệ nhất.** Số liệu thiếu mà không ai biết. Chọn `drop`
|
||||
phải kèm ngưỡng cảnh báo.
|
||||
|
||||
## 2.5 Trạng thái tương đương "màn hình rỗng"
|
||||
|
||||
| Tình huống | Xử lý | Người dùng thấy gì |
|
||||
|---|---|---|
|
||||
| Ngày không có giao dịch nào (chủ nhật, lễ) | Nạp 0 bản ghi — **không phải lỗi** | Báo cáo hiện 0, có nhãn "không có giao dịch" |
|
||||
| Nguồn không phản hồi | | |
|
||||
| Nguồn trả rỗng bất thường | 🔴 Phân biệt với ca trên bằng cách nào? | |
|
||||
|
||||
Phân biệt **"không có dữ liệu"** với **"chưa lấy được dữ liệu"** là bắt buộc. Hai thứ này
|
||||
nhìn giống nhau trên báo cáo nhưng ý nghĩa ngược nhau.
|
||||
|
||||
## 2.6 Đối soát nguồn – đích
|
||||
|
||||
🔴 **Mục bắt buộc, không được bỏ.** Đây là thứ duy nhất chứng minh dữ liệu không bị mất
|
||||
giữa đường.
|
||||
|
||||
| # | Đối chiếu gì | Nguồn | Đích | Sai lệch cho phép | Tần suất | Ai kiểm |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 1 | Số bản ghi | count(pos_api) | count(fact_sales) | 0 | Mỗi lần chạy | Tự động |
|
||||
| 2 | Tổng tiền | sum(total) | sum(amount)×100 | ≤ 1 đơn vị (làm tròn) | Hằng ngày | Tự động |
|
||||
| 3 | Đối chiếu với báo cáo hệ thống cũ | | | | Hằng tháng | Kế toán |
|
||||
|
||||
**Sai lệch vượt ngưỡng thì làm gì, ai chịu trách nhiệm xử lý:** …
|
||||
|
||||
## 2.7 Chạy lại, backfill, dữ liệu đến muộn
|
||||
|
||||
| Câu hỏi | Trả lời |
|
||||
|---|---|
|
||||
| **Chạy lại cùng một ngày hai lần** → kết quả có giống không (idempotent)? | 🔴 Không idempotent ⇒ nói rõ quy trình dọn trước khi chạy lại |
|
||||
| **Backfill** lịch sử N ngày làm thế nào? Mất bao lâu? Ảnh hưởng báo cáo đang chạy không? | |
|
||||
| **Dữ liệu đến muộn** (giao dịch hôm qua tới hôm nay) | Nạp vào ngày phát sinh hay ngày nhận? |
|
||||
| Nạp vào ngày phát sinh ⇒ **báo cáo đã chốt có thay đổi không?** | 🔴 Nếu có, ai được báo |
|
||||
| **Thất bại giữa chừng** | Rollback toàn bộ / tiếp tục từ checkpoint |
|
||||
|
||||
## 2.8 Vận hành
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Chạy tự động lúc | |
|
||||
| Chạy tay được không, ai được chạy | |
|
||||
| Cảnh báo gửi đi đâu | |
|
||||
| Ai trực khi luồng hỏng ban đêm | |
|
||||
| Hỏng bao lâu thì phải báo người dùng | |
|
||||
@@ -0,0 +1,170 @@
|
||||
# PART 2 — biến thể `ml-model`
|
||||
|
||||
> **Dùng khi** `PRODUCT = ml-model` — sản phẩm là mô hình dự đoán: phân loại, hồi quy, xếp
|
||||
> hạng, gợi ý, sinh nội dung. 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**: định nghĩa nhãn rõ ràng và có người gán · tập đánh
|
||||
> giá được chốt và cách ly · mọi metric có **ngưỡng chấp nhận** và ngưỡng đó truy về được
|
||||
> chi phí nghiệp vụ · có hành vi fallback · có tiêu chí giám sát drift.
|
||||
|
||||
---
|
||||
|
||||
## 🔴 Đọc trước: yêu cầu ở đây mang tính xác suất
|
||||
|
||||
Given/When/Then **không mô tả được** phần dự đoán. Không tồn tại AC kiểu *"Given ảnh này,
|
||||
When chạy mô hình, Then trả về đúng nhãn"* — mô hình sẽ sai một tỷ lệ nào đó, và điều đó
|
||||
không phải bug.
|
||||
|
||||
Chia làm hai phần và đối xử khác nhau:
|
||||
|
||||
| Phần | Đặc tả bằng | Ai nghiệm thu |
|
||||
|---|---|---|
|
||||
| **Hệ thống bao quanh** — API, hàng đợi, lưu kết quả, hiển thị, xử lý lỗi | AC Given/When/Then bình thường, 4 nhóm đầy đủ | QA |
|
||||
| **Chất lượng dự đoán** | **Metric + ngưỡng chấp nhận** trên tập đánh giá đã chốt | PO + người sở hữu nghiệp vụ |
|
||||
|
||||
Nhầm hai phần này là lỗi kinh điển: QA viết test case đòi mô hình đúng 100% trên vài mẫu tự
|
||||
chọn, rồi kết luận fail.
|
||||
|
||||
---
|
||||
|
||||
## 2.1 Bài toán
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Loại** | Phân loại nhị phân / đa lớp / Hồi quy / Xếp hạng / Gợi ý / Sinh nội dung |
|
||||
| **Đầu vào** | *(thực thể nào, có những thông tin gì)* |
|
||||
| **Đầu ra** | *(nhãn? điểm số? danh sách xếp hạng? văn bản?)* |
|
||||
| **Quyết định nghiệp vụ nào phụ thuộc đầu ra này** | |
|
||||
| **Ai/cái gì hành động dựa trên đầu ra** | Người xem rồi quyết / Hệ thống tự động thực thi |
|
||||
|
||||
🔴 **Câu cuối quyết định mọi thứ phía sau.** Mô hình chỉ gợi ý cho người xem thì sai số chịu
|
||||
được cao hơn nhiều so với mô hình tự động chặn giao dịch.
|
||||
|
||||
## 2.2 Định nghĩa nhãn / ground truth
|
||||
|
||||
*Mục quan trọng nhất và bị bỏ nhiều nhất. Nhãn định nghĩa lỏng ⇒ mọi metric phía sau vô nghĩa.*
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Nhãn là gì** | *(định nghĩa nghiệp vụ chính xác, không phải "gian lận" chung chung)* |
|
||||
| **Ai gán nhãn** | |
|
||||
| **Gán theo quy tắc nào** | *(kèm ví dụ ca khó)* |
|
||||
| **Hai người gán có ra cùng kết quả không** | *(đo bằng gì, tỷ lệ đồng thuận bao nhiêu)* |
|
||||
| **Nhãn có sẵn tự nhiên không** | *(vd: khách có click hay không — nhãn ngầm)* |
|
||||
| **Độ trễ có nhãn** | 🔴 *(vd: biết một khoản vay xấu phải chờ 6 tháng)* |
|
||||
|
||||
| Ca biên | Gán nhãn thế nào |
|
||||
|---|---|
|
||||
| | |
|
||||
|
||||
## 2.3 Dữ liệu
|
||||
|
||||
| Tập | Khoảng thời gian | Số bản ghi | Tỷ lệ lớp dương | Cách chọn |
|
||||
|---|---|---|---|---|
|
||||
| Train | | | | |
|
||||
| Validation | | | | |
|
||||
| **Test / giữ lại** | | | | 🔴 Chốt trước, **không ai được xem trong lúc phát triển** |
|
||||
|
||||
**Rà rò rỉ dữ liệu (data leakage)** — bốn chỗ hay rò:
|
||||
|
||||
| # | Chỗ rò | Kiểm tra |
|
||||
|---|---|---|
|
||||
| 1 | Trường chỉ tồn tại **sau** khi biết kết quả | ☐ Mọi trường đầu vào đều có tại thời điểm cần dự đoán |
|
||||
| 2 | Chia tập ngẫu nhiên trong khi dữ liệu có thứ tự thời gian | ☐ Chia theo thời gian nếu bài toán có yếu tố thời gian |
|
||||
| 3 | Cùng một thực thể xuất hiện ở cả train và test | ☐ Chia theo nhóm thực thể |
|
||||
| 4 | Chuẩn hoá/thống kê tính trên toàn bộ dữ liệu trước khi chia | ☐ Chỉ tính trên train |
|
||||
|
||||
## 2.4 Metric và ngưỡng chấp nhận
|
||||
|
||||
*Thay cho "acceptance criteria" của phần dự đoán.*
|
||||
|
||||
| ID | Metric | Đo trên | Ngưỡng chấp nhận | Baseline hiện tại | Truy về chi phí nghiệp vụ nào |
|
||||
|---|---|---|---|---|---|
|
||||
| M-01 | Precision @ ngưỡng 0.7 | Test | ≥ 0,85 | Quy tắc tay: 0,62 | Mỗi FP tốn … công xử lý tay |
|
||||
| M-02 | Recall | Test | ≥ 0,70 | 0,45 | Mỗi FN tốn … thiệt hại |
|
||||
| M-03 | Metric theo phân khúc *(xem §2.6)* | Test | Không phân khúc nào < 0,60 | | |
|
||||
|
||||
🔴 **Mỗi ngưỡng phải truy về được chi phí nghiệp vụ.** "Precision ≥ 0,85" chọn từ đâu? Nếu
|
||||
không trả lời được thì đó là con số ai đó thấy đẹp — và nó sẽ bị tranh cãi lại đúng lúc mô
|
||||
hình đạt 0,84.
|
||||
|
||||
**Baseline bắt buộc:** so với **cách làm hiện tại** (quy tắc tay, con người, hoặc đoán theo
|
||||
lớp phổ biến nhất). Mô hình đạt 0,85 mà quy tắc tay đã đạt 0,84 thì không đáng triển khai.
|
||||
|
||||
## 2.5 Ngưỡng quyết định và đánh đổi
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Đầu ra thô** | Điểm số 0–1 |
|
||||
| **Ngưỡng cắt** | *(giá trị, và ai được đổi nó)* |
|
||||
| **Đổi ngưỡng có cần triển khai lại không** | 🔴 Nên là **không** — để nghiệp vụ tự điều chỉnh |
|
||||
|
||||
| Ngưỡng | Precision | Recall | Số ca/ngày phải xử lý tay | Ghi chú |
|
||||
|---|---|---|---|---|
|
||||
| 0,5 | | | | |
|
||||
| 0,7 | | | | ← đề xuất |
|
||||
| 0,9 | | | | |
|
||||
|
||||
Bảng này là bảng **PO đọc để chọn**, không phải BA chọn thay.
|
||||
|
||||
## 2.6 Công bằng và phân khúc
|
||||
|
||||
| Phân khúc | Vì sao cần kiểm riêng | Metric | Ngưỡng | Kết quả |
|
||||
|---|---|---|---|---|
|
||||
| Khách hàng mới (< 30 ngày) | Ít dữ liệu lịch sử | | | |
|
||||
| Theo vùng/chi nhánh | Phân bố khác nhau | | | |
|
||||
|
||||
Metric tổng thể đẹp mà một phân khúc quan trọng rất tệ là tình huống phổ biến, và người dùng
|
||||
sẽ phát hiện ra trước bạn.
|
||||
|
||||
## 2.7 Hành vi khi không chắc chắn & fallback
|
||||
|
||||
*Đây là phần **có** viết được bằng AC bình thường.*
|
||||
|
||||
| Tình huống | Hệ thống làm gì | AC |
|
||||
|---|---|---|
|
||||
| Điểm số nằm trong vùng xám (0,4–0,6) | Chuyển người xử lý tay / gán nhãn "không xác định" | AC-nn |
|
||||
| Thiếu trường đầu vào bắt buộc | 🔴 Đoán đại hay từ chối dự đoán? | AC-nn |
|
||||
| Mô hình không phản hồi / timeout | Trả kết quả mặc định nào? | AC-nn |
|
||||
| Đầu vào ngoài phân phối đã học | | AC-nn |
|
||||
|
||||
🔴 **"Từ chối dự đoán" phải là một đầu ra hợp lệ.** Bắt mô hình luôn trả lời là bắt nó đoán
|
||||
bừa ở đúng những ca nó không biết.
|
||||
|
||||
## 2.8 Giám sát và huấn luyện lại
|
||||
|
||||
| Theo dõi gì | Ngưỡng cảnh báo | Ai được báo | Hành động |
|
||||
|---|---|---|---|
|
||||
| Phân phối đầu vào lệch so với train | | | |
|
||||
| Tỷ lệ dự đoán lớp dương | | | |
|
||||
| Metric trên nhãn thật *(khi có)* | | | |
|
||||
| Tỷ lệ rơi vào vùng xám | | | |
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Tiêu chí huấn luyện lại** | *(theo lịch? theo ngưỡng drift? theo lượng nhãn mới?)* |
|
||||
| **Ai duyệt mô hình mới trước khi thay** | |
|
||||
| **So sánh mô hình mới với cũ bằng gì** | *(cùng tập test đã chốt ở §2.3)* |
|
||||
| **Rollback về mô hình cũ thế nào** | |
|
||||
|
||||
## 2.9 Giải thích được và khiếu nại
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Người bị ảnh hưởng có quyền biết lý do không | *(có yêu cầu pháp lý không)* |
|
||||
| Hiển thị lý do ở mức nào | Không / Yếu tố chính / Đầy đủ |
|
||||
| Người dùng phản đối kết quả thì quy trình nào | |
|
||||
| Lưu vết: đầu vào, phiên bản mô hình, đầu ra, giữ bao lâu | 🔴 Bắt buộc nếu `RIGOR = strict` |
|
||||
|
||||
## 2.10 Nghiệm thu — thay cho UAT thông thường
|
||||
|
||||
| Giai đoạn | Làm gì | Tiêu chí đi tiếp |
|
||||
|---|---|---|
|
||||
| 1. Offline | Đánh giá trên tập test đã chốt | Đạt mọi ngưỡng §2.4 |
|
||||
| 2. Shadow | Chạy song song, **không tác động nghiệp vụ**, so với quyết định của người | Sai lệch trong ngưỡng, tối thiểu … ngày |
|
||||
| 3. Thí điểm | Bật cho một phân khúc nhỏ | Metric online giữ ngưỡng, không có sự cố |
|
||||
| 4. Mở rộng | | |
|
||||
|
||||
🔴 Bỏ bước **shadow** là rủi ro lớn nhất của loại sản phẩm này. Metric offline đẹp mà dữ liệu
|
||||
thật khác phân phối là chuyện xảy ra thường xuyên, và chỉ shadow mới phát hiện được trước
|
||||
khi có thiệt hại.
|
||||
169
.claude/skills/ba-3-specification/templates/srs-part2/screen.md
Normal file
169
.claude/skills/ba-3-specification/templates/srs-part2/screen.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# PART 2 — biến thể `screen`
|
||||
|
||||
> **Dùng khi** `PRODUCT = screen` — sản phẩm có giao diện người dùng (web admin, web app,
|
||||
> app di động). 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** *(thay cho dòng "bảng field" và "wireframe" ở
|
||||
> [workflow.md §2](../../../ba-lifecycle/references/workflow.md))*:
|
||||
> mỗi màn hình có bảng thành phần đủ 8 cột · mỗi field có bảng field đủ 10 cột · hai trạng
|
||||
> thái rỗng khác nhau · mọi thành phần có điều kiện ẩn/khoá/read-only rõ ràng.
|
||||
|
||||
---
|
||||
|
||||
## 2.1 Danh sách màn hình
|
||||
|
||||
| ID | Tên màn hình | Loại | Đường dẫn | Vai trò truy cập | Vào từ đâu | Ra đi đâu |
|
||||
|---|---|---|---|---|---|---|
|
||||
| SCR-01 | | Danh sách / Chi tiết / Form / Modal | | | | |
|
||||
|
||||
Liệt kê **từng màn hình riêng**, kể cả modal và trang lỗi.
|
||||
|
||||
## 2.2 Sơ đồ điều hướng
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
MENU(["Menu"]) --> SCR01["SCR-01 Danh sách"]
|
||||
SCR01 -->|chọn dòng| SCR02["SCR-02 Chi tiết"]
|
||||
SCR01 -->|nút Tạo mới| SCR03["SCR-03 Form"]
|
||||
SCR03 -->|lưu thành công| SCR01
|
||||
SCR03 -->|huỷ / back| SCR01
|
||||
SCR02 -->|back / breadcrumb| SCR01
|
||||
```
|
||||
|
||||
*ID node khớp cột `ID` của bảng §2.1. Nhãn cạnh là **hành động gây điều hướng**.*
|
||||
|
||||
🔴 **Vẽ cả cạnh quay lại.** Sơ đồ chỉ có chiều đi là sơ đồ bỏ sót đúng phần hay lỗi nhất —
|
||||
quay lại có giữ trạng thái danh sách không, huỷ giữa chừng thì đi đâu.
|
||||
|
||||
**Ba câu bắt buộc trả lời cho mỗi màn hình chi tiết/form:**
|
||||
|
||||
| Câu hỏi | SCR-02 | SCR-03 |
|
||||
|---|---|---|
|
||||
| Hai đường quay lại (nút back + breadcrumb)? Quay lại có giữ trạng thái danh sách (trang, bộ lọc)? | | |
|
||||
| Vào bằng URL trực tiếp với id không tồn tại / không có quyền ⇒ hiện gì? | | |
|
||||
| Rời màn hình khi form đang dở ⇒ có cảnh báo mất dữ liệu không? | | |
|
||||
|
||||
---
|
||||
|
||||
## 2.3 SCR-01 — <Tên màn hình>
|
||||
|
||||
**Wireframe:** `WF_SCR-01.png` *(bố cục — hành vi xem bảng bên dưới)*
|
||||
|
||||
### 2.3.1 Bảng thành phần
|
||||
|
||||
| ID | Tên thành phần | Loại | Nhãn (nguyên văn) | Placeholder / Hint | Hành vi & sự kiện | Điều kiện ẩn/khoá | BR |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| C01 | btn_create | Nút | Tạo mới | — | Mở SCR-03 | ❌ ẩn với ROLE-03 | |
|
||||
| C02 | txt_search | Ô nhập | — | Nhập mã hoặc tên | Enter hoặc bấm Tìm mới gọi API | — | |
|
||||
|
||||
**Cột `Điều kiện ẩn/khoá` — chọn rõ một trong ba (quy tắc W7):**
|
||||
|
||||
| Ký hiệu | Nghĩa | Người dùng thấy gì |
|
||||
|---|---|---|
|
||||
| `❌ ẩn` | Không render | Không biết chức năng tồn tại |
|
||||
| `🔒 disable` | Render, không bấm được, **có tooltip nêu lý do** | Biết có, biết vì sao chưa dùng được |
|
||||
| `👁 read-only` | Hiện giá trị, không sửa được | Tra cứu được |
|
||||
|
||||
Ghi "tuỳ quyền" là **chưa đặc tả xong**.
|
||||
|
||||
### 2.3.2 Bảng dữ liệu hiển thị *(màn hình danh sách)*
|
||||
|
||||
| # | Cột | Nguồn dữ liệu | Định dạng | Sắp xếp được | Mặc định | Xử lý khi rỗng | Độ rộng |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 1 | Mã | `code` | Chữ hoa | ✅ | Sắp tăng | `—` | 120px |
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Sắp xếp mặc định** | |
|
||||
| **Số dòng mỗi trang** | mặc định … · tuỳ chọn … |
|
||||
| **Kiểu phân trang** | Offset / Cursor |
|
||||
| **Bấm vào dòng** | Mở chi tiết / Không |
|
||||
|
||||
### 2.3.3 Bảng field *(màn hình có nhập liệu)*
|
||||
|
||||
| ID | Tên field | Kiểu | Bắt buộc | Độ dài / Khoảng | Default | Nguồn giá trị | Validation | Message khi sai | BR |
|
||||
|---|---|---|---|---|---|---|---|---|---|
|
||||
| F01 | | Text | ✅ | 3–20 ký tự (code point UTF-8) | — | Người dùng nhập | | `E-XXX-0001` | BR-0nn |
|
||||
| F02 | | Chọn 1 | ✅ | — | | API `/…`, lọc `active=true`, sắp theo `order` | phải thuộc danh sách | `E-XXX-0002` | |
|
||||
|
||||
🔴 **Bốn cột không được để trống:**
|
||||
|
||||
| Cột | Nếu bỏ trống |
|
||||
|---|---|
|
||||
| **Độ dài/Khoảng** | DB nhận 255, UI không chặn ⇒ lỗi 500 khi dán đoạn dài. Ghi kèm **đơn vị** (ký tự? byte?) |
|
||||
| **Default** | Mỗi màn hình một kiểu, báo cáo lệch vì bản ghi cũ null |
|
||||
| **Nguồn giá trị** | Dropdown lấy từ đâu, **lọc theo gì**, **sắp xếp thế nào** |
|
||||
| **Message khi sai** | Dev tự viết ⇒ mỗi màn hình một giọng, không dịch được |
|
||||
|
||||
### 2.3.4 Sơ đồ luồng *(bắt buộc khi hành động chạm ≥ 3 bên)*
|
||||
|
||||
*Chỉ vẽ khi luồng đi qua người dùng → giao diện → hệ thống → bên thứ ba, hoặc có bước bất
|
||||
đồng bộ. Luồng đơn giản (bấm Lưu, gọi 1 API) thì bảng §2.3.5 là đủ.*
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor U as Người dùng
|
||||
participant FE as Giao diện
|
||||
participant BE as Hệ thống
|
||||
participant EXT as Hệ thống ngoài
|
||||
|
||||
U->>FE: Bấm Lưu
|
||||
FE->>FE: Validate phía giao diện
|
||||
FE->>BE: POST /… (khoá nút Lưu)
|
||||
BE->>EXT: Kiểm tra … (timeout 3s)
|
||||
alt Phản hồi kịp
|
||||
EXT-->>BE: 200 OK
|
||||
BE-->>FE: 200 + id
|
||||
FE-->>U: Toast "Đã lưu", về danh sách
|
||||
else Timeout / lỗi
|
||||
EXT--xBE: timeout
|
||||
BE-->>FE: 503 · E-XXX-0503
|
||||
FE-->>U: Báo lỗi, GIỮ NGUYÊN dữ liệu đã nhập, mở lại nút Lưu
|
||||
end
|
||||
```
|
||||
|
||||
🔴 **Bắt buộc vẽ cả nhánh lỗi** (`alt`/`else`). Sequence chỉ có luồng thành công là vi phạm
|
||||
quy tắc W4, và đó chính là nhánh dev hay tự bịa.
|
||||
|
||||
**Bảng đi kèm** *(quy tắc W13 — sơ đồ không nói được mã lỗi và ngưỡng)*:
|
||||
|
||||
| Bước | Mô tả | Timeout | Thất bại thì sao | Mã lỗi | AC |
|
||||
|---|---|---|---|---|---|
|
||||
| 3 | Gửi form lên hệ thống | 30s | Giữ dữ liệu, mở lại nút | `E-XXX-0503` | AC-09 |
|
||||
| 4 | Kiểm tra với hệ thống ngoài | 3s | Cho lưu và đồng bộ sau / chặn? | | |
|
||||
|
||||
*Số ở cột **Bước** là số `autonumber` trong sơ đồ.*
|
||||
|
||||
### 2.3.5 Hành động trên màn hình
|
||||
|
||||
| Hành động | Điều kiện được phép | Xác nhận trước khi làm | Kết quả thành công | Kết quả thất bại | Vai trò | AC |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Lưu | Form hợp lệ | Không | Toast + về danh sách | Giữ nguyên dữ liệu đã nhập, hiện lỗi | | AC-01 |
|
||||
| Xoá | Trạng thái = Nháp | ✅ Modal + **nhập lý do** | Toast + xoá khỏi danh sách | Hiện lỗi, không xoá | | AC-07 |
|
||||
|
||||
🔴 **Thất bại phải giữ nguyên dữ liệu người dùng đã nhập.** Xoá trắng form sau lỗi mạng là
|
||||
lỗi trải nghiệm nghiêm trọng và rất hay xảy ra khi spec không nói.
|
||||
|
||||
### 2.3.6 Trạng thái rỗng, đang tải, lỗi
|
||||
|
||||
| Trạng thái | Hiển thị gì | Text nguyên văn | Nút hành động |
|
||||
|---|---|---|---|
|
||||
| Đang tải | | | |
|
||||
| **Chưa có dữ liệu nào** | | "Chưa có bản ghi nào. Tạo bản ghi đầu tiên?" | ✅ Tạo mới |
|
||||
| **Bộ lọc không khớp** | | "Không tìm thấy kết quả phù hợp với bộ lọc." | ✅ Xoá lọc |
|
||||
| Lỗi tải dữ liệu | | | ✅ Thử lại |
|
||||
|
||||
🔴 Hai trạng thái rỗng **phải khác nhau**. Dùng chung một câu khiến người dùng tưởng mất dữ liệu.
|
||||
|
||||
---
|
||||
|
||||
## Lưu ý cho `PRODUCT = screen` + app di động B2C
|
||||
|
||||
Biến thể này viết cho phần mềm nghiệp vụ có vai trò. Với app tiêu dùng B2C, ba chỗ lệch:
|
||||
|
||||
| Chỗ | Điều chỉnh |
|
||||
|---|---|
|
||||
| `RBAC` ở GĐ2 | Thường chỉ 1–2 vai trò ⇒ ma trận gần như rỗng. Ghi rõ thay vì bỏ, và chuyển trọng tâm sang **phạm vi dữ liệu của chính người dùng** |
|
||||
| Elicitation ở GĐ1 | Không phỏng vấn được hàng vạn người ⇒ dựa vào analytics + phỏng vấn sâu vài người + khảo sát |
|
||||
| UAT ở GĐ4 | "Đúng người dùng thật" không scale ⇒ thay bằng **usability test 5–8 người + beta có giám sát**, tiêu chí pass đổi thành tỷ lệ hoàn thành tác vụ |
|
||||
Reference in New Issue
Block a user