PRD: Configuration & chính sách xóa
| Module | Commerce (CORE-03) | PRD ID | PRD-CFG-001 |
| Status | Shipped | Owner | Commerce squad |
| Date | 2026-04-03 | Version | v1.0 |
| Packages | @nx/commerce | URD | DEL · CFG |
TL;DR
Cấp cho mỗi merchant một policy xóa có thể cấu hình - hai cờ quyết định xem một danh mục còn sản phẩm liên kết có được xóa hay không và việc xóa một danh mục có cascade xuống sản phẩm của nó hay không - được hậu thuẫn bởi kho configuration mã hóa theo từng merchant. Owner kiểm soát hành vi xóa từ merchant, và mọi đường xóa (merchant, danh mục, sản phẩm, biến thể, sale channel) đều đi qua một guard duy nhất, nên một thao tác xóa nhầm không thể âm thầm để mồ côi hay xóa sạch nội dung.
1. Context & Problem
Commerce đã lưu settings theo từng merchant - thông tin xác thực payment-provider và config tích hợp - dưới dạng các mục key-value mã hóa qua ConfigurationService và SettingsRepository (vùng Configuration / CFG). Cái còn thiếu là một câu trả lời mà merchant có thể cấu hình cho một câu hỏi mang tính hủy hoại: điều gì sẽ xảy ra khi một owner xóa một danh mục vẫn còn sản phẩm, hay xóa một sản phẩm có biến thể?
Khi không có policy, hành vi xóa bị hard-code và đồng nhất. Một owner không thể diễn đạt "chặn xóa một danh mục vẫn còn sản phẩm" so với "để thao tác xóa cascade xuống các sản phẩm", và không có một nơi duy nhất nào áp dụng guard một cách nhất quán trên các đường xóa của merchant, danh mục, sản phẩm, biến thể và sale channel. Với tệp khách HKD/SME mà KICKO nhắm tới, một cascade nhầm - hay một mồ côi nhầm - là một sự cố mất dữ liệu.
Increment này thêm một Chính sách xóa (DEL) theo từng merchant, được lưu trong cùng kho settings mã hóa và gắn một guard duy nhất vào mọi đường xóa nội dung của merchant.
2. Goals & Non-Goals
Goals
- Lưu một policy xóa theo từng merchant với hai cờ -
strictCategoryDeletionvàcascadeProductDeletion- mặc định là{ strict: true, cascade: false }. - Cung cấp xem và cập nhật policy trên merchant (
GET/POST /{id}/deletion-policy), được kiểm soát bởi permissionMerchant.findDeletionPolicy/Merchant.updateDeletionPolicy. - Áp dụng policy trên các đường xóa: chặn xóa danh mục khi còn sản phẩm và strict đang bật; cascade xóa sản phẩm xuống biến thể khi cascade đang bật; chặn xóa một sản phẩm hay biến thể đã được bán.
- Tái sử dụng kho configuration mã hóa theo từng merchant (
SettingsRepository) làm nơi chứa policy - không thêm column hay bảng mới.
Non-Goals
- Xóa cứng (hard-delete) bất kỳ entity nào - mọi thao tác xóa vẫn là soft-delete (URD C-03).
- CRUD danh mục / sale channel độc lập ngoài aggregate merchant (Dự kiến).
- Cờ xóa cứng campaign - xóa campaign nằm ngoài phạm vi.
- Chia sẻ configuration giữa các tổ chức.
3. Success Metrics
| Metric | Mục tiêu / tín hiệu |
|---|---|
| Độ phủ guard | 100% thao tác xóa nội dung merchant (merchant, danh mục, sản phẩm, biến thể, sale channel) đi qua guard xóa duy nhất |
| An toàn dữ liệu | Không có cascade nhầm hay nội dung mồ côi; một sản phẩm/biến thể đã bán không bao giờ bị xóa cứng |
| Mức áp dụng policy | Số merchant tùy chỉnh policy mặc định (strict bật / cascade tắt) khi quy trình của họ khác đi |
| Mặc định đúng | Merchant mới khởi đầu với { strict: true, cascade: false } mà không cần thiết lập thủ công |
4. Personas & Use Cases
| Persona | Mục tiêu trong tính năng này |
|---|---|
| Owner | Quyết định cách các thao tác xóa cascade cho merchant của mình; xem và cập nhật policy |
| Manager | Xóa danh mục/sản phẩm trong hàng rào an toàn mà policy áp dụng |
| Hệ thống (các đường xóa) | Áp dụng policy đồng nhất để không đường nào bỏ qua được guard |
Core scenarios: một owner xem policy xóa của merchant → bật/tắt strictCategoryDeletion / cascadeProductDeletion → các thao tác xóa danh mục/sản phẩm/biến thể/sale channel sau đó được đánh giá theo policy → một thao tác xóa được guard sẽ hoặc chặn, hoặc cascade, hoặc gỡ liên kết theo các cờ đã cấu hình.
5. User Stories
- Là một owner, tôi muốn có một policy xóa theo từng merchant, để hành vi xóa khớp với cách doanh nghiệp tôi đối xử với danh mục và sản phẩm.
- Là một owner, tôi muốn chặn xóa một danh mục vẫn còn sản phẩm, để tôi không bao giờ mất sản phẩm vì xóa nhóm chứa chúng.
- Là một owner, tôi muốn có tùy chọn cascade thao tác xóa danh mục xuống các sản phẩm của nó, để có thể dọn sạch cả một nhóm trong một thao tác khi tôi cố ý làm vậy.
- Là một manager, tôi muốn các thao tác xóa sẽ hủy hoại item đã bán bị từ chối, để dữ liệu bán hàng lịch sử còn nguyên vẹn.
- Là một owner, tôi muốn policy được lưu cùng với settings mã hóa hiện có của tôi, để có một nơi duy nhất cho configuration của merchant.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | Mỗi merchant mang một policy xóa có thể cấu hình với hai cờ, mặc định là { strictCategoryDeletion: true, cascadeProductDeletion: false } | URD-DEL-001 |
| FR-2 | strictCategoryDeletion: khi true, chặn xóa danh mục nếu danh mục có sản phẩm liên kết; ngược lại gỡ liên kết | URD-DEL-002 |
| FR-3 | cascadeProductDeletion: khi true, xóa một danh mục sẽ cascade xuống các sản phẩm của nó | URD-DEL-003 |
| FR-4 | Owner có thể xem và cập nhật policy qua GET / POST /{id}/deletion-policy, kiểm soát bởi Merchant.findDeletionPolicy / Merchant.updateDeletionPolicy | URD-DEL-004 |
| FR-5 | Xóa sản phẩm cascade xuống biến thể khi cascade bật, ngược lại chặn nếu còn biến thể; khẳng định sản phẩm chưa được bán | URD-DEL-002..003 |
| FR-6 | Xóa biến thể và sale channel khẳng định chưa-bán và dọn dẹp các phụ thuộc | URD-DEL-002 |
| FR-7 | Xóa merchant khẳng định không có merchant con và không còn nội dung, rồi dọn dẹp meta-link và settings | URD-DEL-001 |
| FR-8 | Policy được lưu theo từng merchant qua kho settings mã hóa, nhóm theo vùng | URD-CFG-001..003 |
Toàn văn requirement và tiêu chí nghiệm thu nằm trong Commerce URD. PRD này tham chiếu chúng thay vì lặp lại.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Toàn vẹn dữ liệu | Mọi thao tác xóa là soft-delete; một sản phẩm/biến thể đã bán không bao giờ bị xóa; các thao tác xóa được guard để nội dung không bao giờ bị mồ côi âm thầm hay cascade trái policy |
| Guard duy nhất | Một service tập trung mọi guard xóa nội dung merchant; không đường xóa nào bỏ qua nó |
| Bảo mật | Policy được lưu trong kho settings mã hóa theo từng merchant; configuration nhạy cảm được mã hóa khi lưu trữ |
| Tenancy & authz | Xem/cập nhật policy scope theo từng merchant và kiểm soát bởi Merchant.findDeletionPolicy / Merchant.updateDeletionPolicy |
| Nhất quán | Cascade và dọn dẹp diễn ra trong một thao tác duy nhất; một thao tác xóa dở dang không để lại tham chiếu treo |
| i18n | Nhãn/thông báo hiển thị cho người dùng là song ngữ ({ en, vi }) |
8. UX & Flows
Màn hình chính (trong apps/client): vùng settings của merchant hiển thị các công tắc deletion-policy, và các hộp xác nhận xóa trên danh mục / sản phẩm / biến thể / sale channel làm rõ kết quả của guard.
9. Data & Domain
| Entity | Vai trò |
|---|---|
Merchant | Sở hữu policy xóa; các đường xóa nội dung của nó đi qua guard |
| Config policy xóa | Policy hai cờ (strictCategoryDeletion, cascadeProductDeletion) lưu theo từng merchant |
| Settings store | Configuration key-value mã hóa theo từng merchant (principalType = Merchant), nhóm theo vùng; nơi chứa policy |
Category / Product / ProductVariant / SaleChannel | Nội dung mà các thao tác xóa được đánh giá theo policy |
Chỉ mang tính khái niệm - schema và bất biến đầy đủ nằm trong commerce domain model.
10. Dependencies & Assumptions
Phụ thuộc vào
- Kho configuration (URD-CFG-001..003) - cơ chế settings mã hóa theo từng merchant lưu policy.
- Merchant (URD-MER) - policy được merchant sở hữu và đọc từ merchant.
- Danh mục / Sản phẩm - nội dung mà policy guard khi xóa.
Giả định
- Kho settings mã hóa (
ConfigurationService,SettingsRepository) đã tồn tại và được tái sử dụng, không xây lại. - Một merchant luôn phân giải ra một policy - mặc định
{ strict: true, cascade: false }áp dụng khi chưa đặt. - Tình trạng đã-bán của một sản phẩm/biến thể có thể xác định tại thời điểm xóa.
11. Risks & Open Questions
| Rủi ro / câu hỏi | Giảm thiểu / trạng thái |
|---|---|
| Một thao tác cascade xóa nhiều hơn dự định | Cascade mặc định tắt; cascade chỉ chạy khi owner bật rõ ràng |
| Một đường xóa bỏ qua guard | Mọi thao tác xóa nội dung merchant đi qua service guard duy nhất |
| Xóa nội dung gắn với dữ liệu bán hàng lịch sử | Sản phẩm/biến thể đã bán được khẳng định chưa-bán và bị từ chối |
| Settings mã hóa không khả dụng tại thời điểm xóa | Đọc policy lùi về mặc định an toàn (strict bật, cascade tắt) |
12. Release Plan & Launch Criteria
| Khía cạnh | Kế hoạch |
|---|---|
| Phase | P2 (quản lý đầy đủ) - xem URD feature catalog |
| Rollout | Tất cả merchant; không feature flag |
| Migration | Không - policy lưu trong kho settings hiện có; mặc định áp dụng khi chưa đặt |
| Tiêu chí ra mắt | Endpoint xem/cập nhật được kiểm soát permission; guard chặn-strict, cascade và chưa-bán được kiểm chứng trên mọi đường xóa |
| Giám sát | Tỷ lệ xóa bị chặn, lượng xóa cascade, cảnh báo bỏ qua guard (phải bằng 0) |
13. FAQ
Policy xóa mặc định là gì? { strictCategoryDeletion: true, cascadeProductDeletion: false } - tổ hợp an toàn nhất: một danh mục còn sản phẩm không thể bị xóa, và xóa một danh mục không bao giờ cascade xuống sản phẩm trừ khi bạn bật cascade.
Policy được lưu ở đâu? Trong kho settings mã hóa theo từng merchant hiện có (SettingsRepository), không phải một column hay bảng mới.
Policy có bao giờ cho phép xóa cứng dữ liệu không? Không - mọi thao tác xóa vẫn là soft-delete (URD C-03). Policy chỉ kiểm soát hành vi chặn / cascade / gỡ liên kết.
Tôi có thể xóa một sản phẩm đã được bán không? Không - guard khẳng định sản phẩm (và mọi biến thể) chưa bán trước khi xóa, nên dữ liệu bán hàng lịch sử còn nguyên vẹn.
Cái này có bao gồm xóa campaign không? Không - xóa campaign nằm ngoài phạm vi của increment này.
References
- URD: Commerce - Deletion Policy · Configuration
- Dựa trên: Merchant · Categories
- Module: Commerce - tổng quan + traceability
- Developer: @nx/commerce · domain model