update skill and docs
This commit is contained in:
157
.claude/skills/sa-2-architecture/templates/domain-model.md
Normal file
157
.claude/skills/sa-2-architecture/templates/domain-model.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# DOM — Domain Model — <PROJECT>
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Version** | 1.0 |
|
||||
| **Date** | YYYY-MM-DD |
|
||||
| **Author** | <SA> (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 <Bounded context>
|
||||
|
||||
<!-- archify: mermaid-only -->
|
||||
```mermaid
|
||||
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 |
|
||||
|---|---|---|---|---|---|
|
||||
Reference in New Issue
Block a user