Hai yêu cầu này chỉ mâu thuẫn nếu hiểu "xoá" là DELETE mọi dòng. Cách giải quyết là tách dữ liệu định danh khỏi dữ liệu giao dịch, rồi ẩn danh hoá phần định danh thay vì xoá bản ghi nghiệp vụ.
Ba mức xử lý:
- Xoá cứng: dữ liệu chỉ phục vụ cá nhân — avatar, địa chỉ giao hàng đã lưu, thiết bị, session, tuỳ chọn, log hành vi.
- Ẩn danh hoá: bản ghi phải giữ vì lý do kế toán, thuế, chống gian lận. Thay email/tên/điện thoại bằng giá trị vô danh, giữ nguyên order_id, số tiền, thời điểm.
- Giữ nguyên có thời hạn: hoá đơn theo quy định lưu trữ chứng từ, tranh chấp đang mở, nghĩa vụ pháp lý.
UPDATE users SET
email = 'deleted+' || id || '@example.invalid',
full_name = 'Deleted user', phone = NULL,
avatar_url = NULL, deleted_at = now()
WHERE id = $1;Giữ lại dòng users với khoá chính giúp mọi foreign key từ orders không gãy, còn thông tin cá nhân thì đã biến mất.
Luồng thực thi — đây là phần phân biệt câu trả lời hời hợt và câu trả lời có kinh nghiệm:
1. Xác thực lại người yêu cầu, rồi chuyển tài khoản sang pending_deletion và vô hiệu toàn bộ session/token ngay.
2. Chờ một cửa sổ huỷ yêu cầu (7-30 ngày) đề phòng tài khoản bị chiếm hoặc user đổi ý.
3. Job xoá chạy theo danh sách nơi dữ liệu đã lan tới, không chỉ bảng chính: bản sao ở search index, cache, object storage (ảnh đã upload), hàng đợi, data warehouse, và các dịch vụ bên thứ ba (CRM, email marketing, analytics) — mỗi nơi cần một API xoá riêng.
4. Bản sao lưu (backup) không xoá được từng dòng: ghi lại yêu cầu, và khi khôi phục backup phải chạy lại danh sách xoá. Nêu được điểm này là ghi điểm.
5. Ghi vào audit log rằng đã xoá — nhưng bản thân log không được chứa lại dữ liệu vừa xoá.
Soft delete thông thường (deleted_at để ẩn khỏi UI) không phải là xoá dữ liệu cá nhân; đừng trả lời gộp hai thứ làm một.