Files
sys-analysis-design/.claude/skills/sa-2-architecture/templates/physical-data-model.md
2026-09-22 13:46:36 +07:00

8.5 KiB
Raw Blame History

PDM — Physical Data Model —

Version 1.0
Date YYYY-MM-DD
Author (skill sa-2-architecture)
Status 🟡 Draft
Approved by Tech Lead: — · DBA: — · BE Lead: —
Source DAT_… v1.0 · DOM_… v1.0 · SRS_… §2.3.3 (bảng field) · BR_… của BA · SEC_… §5
Scope (kho nào — một PDM mỗi kho vật lý, hoặc một file nhiều §2.x)
Confidence 🟡

Change Log

Version Date Người sửa Thay đổi ADR / MIG
1.0 Bản đầu MIG-001

Thẩm quyền: file DDL trong 02-architecture/schema/ thắng bảng ở đây khi lệch — bảng là bản đọc cho người, DDL là thứ chạy. Lệch ⇒ lỗi tài liệu, sửa bảng.

Vì sao có tài liệu này: DAT dừng ở "thực thể, quan hệ, ai sở hữu". Không có PDM, mỗi dev tự đặt kiểu cột, độ dài, nullable, index — và bảng field của BA (FLD-…) chặn ở tầng UI nhưng không chặn ở tầng DB, hoặc ngược lại.


1. Kho và quy ước

Kho (tên · loại · phiên bản, ví dụ PostgreSQL 16 · RDS) — quyết định ở DAT §3 / ADR-nnn
Schema / namespace (schema-per-module theo ADR-nnn: identity, catalog, cart_order…)
Công cụ migration (Flyway / Liquibase / Prisma Migrate / Alembic / golang-migrate…) — ADR-nnn
Đặt tên bảng snake_case số ít · PK id · FK <bảng>_id · index ix_<bảng>_<cột> · unique ux_… · FK constraint fk_<bảng>_<cột>
Kiểu id uuid v7 / bigint identity — ADR-nnn; truyền qua API dạng string (ICD §1)
Thời gian timestamptz, lưu UTC; cột created_at, updated_at bắt buộc mọi bảng
Tiền numeric(18,2) + cột currency char(3) hoặc value object cố định VND — theo DOM §2 Money
Xoá soft delete (deleted_at) cho bảng có yêu cầu retention ở DAT §5; hard delete cho bảng còn lại — ghi từng bảng ở §2
Ký tự UTF-8; độ dài varchar(n) tính theo code point, khớp bảng field của SRS (W3)

2. Bảng — TBL-nnn

Một mục mỗi bảng. Mỗi bảng phải truy về một thực thể DAT §1 và một class DOM §2; bảng không truy được là bảng thừa hoặc thiếu mô hình.

2.1 TBL-001 · cart_order.order ← Order (DAT §1.3 · DOM §2.1)

Chủ sở hữu CMP-04 — chỉ component này được ghi (DAT §1)
Ước lượng … bản ghi/năm 1 · … năm 3 (DAT §2.2) · tăng …/tháng
Xoá soft delete · retention … năm (DAT §5)
Phân mảnh không / theo placed_at tháng (DAT §7.4 · ADR-nnn)

Cột

Cột Kiểu Null Default Ràng buộc PII (SEC §5) Nguồn (FLD/BR) Ghi chú
id uuid ✗ gen_uuid_v7() PK — —
order_number varchar(20) ✗ — ux_order_order_number — BR-010 định dạng khoá nghiệp vụ
customer_id uuid ✓ NULL (không FK vật lý — khác schema, DAT §1) 🔶 gián tiếp FLD-SCR04-01 null = Guest
total_amount numeric(18,2) ✗ — CHECK (total_amount >= 0) — BR-014 = Σ order_seller.subtotal_amount
status varchar(32) ✗ 'pending' CHECK (status IN (…)) theo BR §3 — BR-021 enum ở tầng app, CHECK ở DB
idempotency_key varchar(64) ✗ — ux_order_idempotency_key — ADR-004 khoá của POST /v1/checkout
placed_at timestamptz ✗ now() —
created_at / updated_at timestamptz ✗ now() — quy ước §1
deleted_at timestamptz ✓ NULL — quy ước §1 soft delete

🔴 Bốn ô không được trống ở bất kỳ cột nào: kiểu có độ dài/độ chính xác · Null · Default · Nguồn. Trùng nguyên tắc "bảng ràng buộc cụ thể tới mức không cần hỏi lại" của SRS PART 2.

Index và khoá ngoại

Tên Loại Cột Vì sao (QAS/truy vấn DAT §7.1) Kích cỡ ước lượng
ix_order_customer_placed btree (customer_id, placed_at DESC) lịch sử đơn · QAS-006
fk_order_seller_order FK order_seller.order_id → order.id ON DELETE RESTRICT

🔴 Index không truy về truy vấn nào ⇒ bỏ. Truy vấn trong DAT §7.1 không có index ⇒ thiếu.

2.2 TBL-002 · <schema>.<bảng> ← <Entity>

(cùng cấu trúc)


3. Ánh xạ thực thể → bảng

Bảng đối chiếu bắt buộc, sa-conformance đọc bảng này.

Thực thể (DAT §1) Class (DOM §2) Bảng (TBL) Quan hệ 1 thực thể → n bảng? Ghi chú
Order Order TBL-001 1→1
Order × Seller OrderSeller TBL-002 tách theo ADR-003
Cart (Redis) Cart (không phải bảng — key schema §5)

Thực thể không có dòng ⇒ 🔴 chặn AG2. Bảng không có thực thể ⇒ bảng thừa hoặc thiếu mô hình.


4. DDL và migration — MIG-nnn

Vị trí: sa-output/<PROJECT>/02-architecture/schema/<kho>/

schema/<kho>/
├── V001__init_<schema>.sql          ← MIG-001: toàn bộ bảng §2, chạy được từ CSDL rỗng
├── V001__init_<schema>.down.sql     ← rollback tương ứng (bắt buộc)
├── V002__<thay-doi>.sql             ← mỗi thay đổi sau baseline một cặp up/down
├── V002__<thay-doi>.down.sql
└── seed/
    └── R__reference_data.sql        ← dữ liệu tham chiếu (enum bảng, cấu hình) — idempotent
MIG File Nội dung Rollback Dữ liệu phát sinh trong lúc chạy Thời lượng ước lượng Chạy trên
MIG-001 V001__init_cart_order.sql tạo TBL-001..0nn V001__….down.sql drop ngược thứ tự FK không (CSDL rỗng) < 1 phút dev/stg/prod
MIG-002

🔴 Migration không có down là migration một chiều (DAT §8). Với migration đổi dữ liệu (không chỉ DDL) phải ghi thêm: cách đối chiếu sau khi chạy, ngưỡng chênh lệch chấp nhận, ai duyệt.

Quy ước file SQL: một câu lệnh một dòng logic · comment đầu file ghi MIG-nnn, TBL liên quan, ADR · không dùng lệnh phụ thuộc quyền superuser · CREATE … IF NOT EXISTS chỉ trong seed.

Kiểm bằng máy (người điều phối chạy, ghi kết quả vào đây):

Kiểm Lệnh Kết quả
Up từ rỗng <tool> migrate trên container CSDL rỗng ☐
Down toàn bộ rồi up lại <tool> migrate down --all && <tool> migrate ☐
Mọi bảng §2 có trong DDL grep -c "CREATE TABLE" V001*.sql = số TBL ☐
Mọi cột PII có cờ ở SEC §5 đối chiếu cột 🔶/✅ PII với SEC §5 ☐

Kho Key / collection Cấu trúc value TTL Invalidation (DAT §7.4) Kích cỡ ước lượng
Redis CMP-15 cart:{sessionId} JSON CartSnapshot (DOM §2) 7 ngày write-through khi PATCH/DELETE ≤ 50 dòng × …

6. Dữ liệu tham chiếu và seed

Bảng Nguồn dữ liệu Ai duy trì Cách nạp Idempotent

7. Đối chiếu

Kiểm Kết quả Hành động
DAT thực thể → TBL (§3) n/n
FLD-* trong SRS PART 2 → cột (kiểu/độ dài/null khớp) n/n lệch ⇒ OQ, BA cập nhật SRS hoặc PDM sửa
Cột PII → SEC §5 có dòng n/n
Truy vấn DAT §7.1 → index n/n
Mọi MIG có down n/n

8. Giả định & Ngoài phạm vi

Giả định:

ID Giả định Cách xác minh Nếu sai

Ngoài phạm vi:

  • (ví dụ: không thiết kế kho phân tích/BI — ngoài CON-…)

9. Open Questions

ID Câu hỏi Hỏi ai Từ ngày Chặn gì Hệ quả nếu trả lời ngược