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

5.9 KiB

DOM — Domain Model —

Version 1.0
Date YYYY-MM-DD
Author (skill sa-2-architecture)
Status 🟡 Draft
Approved by Tech Lead: — · BE Lead: —
Source SAD_… v1.0 · DAT_… v1.0 · BR_… của BA · BACKLOG_… của BA
Scope
Confidence 🟡

Change Log

Version Date Người sửa Thay đổi ADR
1.0 Bản đầu —

Thẩm quyền khi mâu thuẫn: sơ đồ thắng về quan hệ và luồng; bảng thắng về ràng buộc và con số. BR của BA là nguồn của mọi invariant ở đây; DOM chỉ nói invariant đó sống ở class nào. Lệch với BR ⇒ OQ, không tự sửa rule.

Vì sao có tài liệu này: DAT nói ai sở hữu dữ liệu gì, PDM nói bảng vật lý trông thế nào. Không có DOM, dev tự suy ra aggregate từ bảng — và mỗi người suy một kiểu, transaction boundary lệch nhau, invariant bị kiểm ở ba chỗ hoặc không chỗ nào.


1. Bounded context và aggregate

Một dòng mỗi aggregate. Aggregate là ranh giới transaction: mọi invariant trong aggregate được kiểm trong một lần ghi; giữa các aggregate chỉ có eventual consistency (DAT §4).

Bounded context CMP-nn sở hữu Aggregate root Entity con Value object Invariant chính (BR) Sự kiện phát ra
Cart & Order CMP-04 Order OrderSeller, OrderItem Money, Address BR-014 tổng tiền = Σ dòng OrderPlaced

🔴 Một entity thuộc đúng một aggregate. Entity xuất hiện ở hai aggregate ⇒ một trong hai chỉ được giữ id tham chiếu (không giữ object), ghi rõ ở §3.


2. Class diagram theo bounded context

Một sơ đồ mỗi bounded context, ≤ 12 class. Chỉ vẽ thuộc tính nghiệp vụ và phương thức đổi trạng thái; getter/setter, DTO, repository không vẽ ở đây (thuộc AGD).

2.1

classDiagram
    class Order {
        +OrderId id
        +CustomerId customerId
        +Money totalAmount
        +OrderStatus status
        +place(cart) Order
        +cancel(reason)
    }
    class OrderSeller {
        +SellerId sellerId
        +Money subtotal
        +confirm()
    }
    class OrderItem {
        +ProductVariantId variantId
        +int quantity
        +Money unitPriceSnapshot
    }
    class Money {
        <<value object>>
        +decimal amount
        +Currency currency
    }
    Order "1" *-- "1..*" OrderSeller : splits into
    OrderSeller "1" *-- "1..*" OrderItem : contains
    Order ..> Money
    OrderItem ..> Money

Bảng đi kèm (W13 — sơ đồ không nói được invariant kiểm ở đâu và ai được gọi):

Class Loại Thuộc tính nghiệp vụ Invariant (BR) Kiểm ở phương thức Ai được gọi (ROLE/CMP) Bảng PDM
Order aggregate root totalAmount, status BR-014, BR-021 place(), cancel() ROLE-01 qua IF-002 TBL-012 order
Money value object amount, currency BR-003 không âm constructor — cột *_amount

Quan hệ: *-- composition (cùng transaction) · o-- aggregation (khác vòng đời) · ..> phụ thuộc · --> tham chiếu bằng id sang aggregate khác.

2.2 <Bounded context tiếp theo>

(cùng cấu trúc)


3. Tham chiếu xuyên aggregate

Chỗ hay sai nhất: giữ object của aggregate khác thay vì id, rồi load cả cây.

Từ aggregate Tham chiếu tới Bằng gì Đọc dữ liệu kia bằng Trễ chấp nhận (DAT §4)
Order Customer CustomerId IF-0nn / read model eventual ≤ …
OrderItem ProductVariant ProductVariantId + snapshot giá/tên snapshot lúc đặt không cần đồng bộ

🔴 Snapshot hay tham chiếu sống? Giá, tên, địa chỉ lúc giao dịch phải là snapshot (thay đổi sau đó không được đổi lịch sử). Ghi rõ từng trường, đây là nguồn của nhiều bug đối soát.


4. Vòng đời trạng thái → class

Bảng chuyển trạng thái nằm ở BR §3; đây chỉ map xuống class và phương thức.

Entity Enum trạng thái Thuộc BR Phương thức chuyển Guard kiểm ở đâu Ghi vết (SEC §audit)
Order OrderStatus BR-021 confirm(), cancel() trong aggregate order_status_history

5. Domain event

Sự kiện Phát từ aggregate Khi nào Payload tối thiểu Người nhận (CMP) Contract (CTR)
OrderPlaced Order place() commit xong orderId, customerId, sellerIds[], totalAmount CMP-06, CMP-09 contracts/asyncapi/order-events.yaml#OrderPlaced

🔴 Payload event không chứa PII ngoài id trừ khi SEC §5 cho phép đích danh.


6. Đối chiếu

Kiểm Kết quả Hành động
Mọi thực thể trong DAT §1 có class ở §2 n/n
Mọi BR loại "ràng buộc dữ liệu"/"tính toán" có class kiểm n/n
Mọi entity có trạng thái ở BR §3 có enum + phương thức ở §4 n/n
Mọi class root có bảng trong PDM §2 n/n (điền khi PDM xong)

7. 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 mô hình hoá module báo cáo — chỉ đọc, dùng read model của DAT §4)

8. 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