ADR-0001. EventBus in-process + BullMQ cho đồng bộ sản phẩm đa merchant
| Trường | Giá trị |
|---|---|
| Status | Accepted |
| Ngày | 2026-03-01 |
| Người quyết định | commerce-team |
| Thay thế | — |
Bối cảnh
- Một sản phẩm được tạo/cập nhật cho một merchant thường phải được sao chép tới các merchant bổ sung của cùng organizer (chuỗi cửa hàng dùng chung catalog).
- Việc create/update aggregate đã là một transaction DB duy nhất (Product + Info + identifiers + liên kết kênh + variant mặc định). Sao chép động chạm nhiều hàng qua N merchant và có thể chậm.
- Nó không được chặn hoặc làm fail response aggregate chính, và phải sống sót qua lỗi một phần (bản sao của một merchant fail không nên rollback bản chính).
- Commerce đã chạy sẵn một kết nối BullMQ Redis và một vai trò worker.
Quyết định
Tách sao chép thành hai giai đoạn kết nối bởi một EventBus in-process (eventemitter3) và một hàng đợi BullMQ:
ProductCreateService/ProductUpdateServicecommit aggregate chính, rồi phátproduct.aggregate.{created,updated}trên EventBus (khóaEVENT_EMITTERtrongsrc/common/event.ts).ProductAggregateCreatedListener/ProductAggregateUpdatedListenerxử lý sự kiện. Listenercreatedcũng đánh dấu bước onboarding merchantPRODUCT. Cả hai gọipushJobToQueue()— nhưng chỉ khisyncMerchantIds.length > 0.- Job rơi vào
SYNC_PRODUCT_QUEUE(@nx/commerce/sync-product-queue) trên BullMQ Redis. SyncProductWorkertiêu thụ nó (tên job.createvs.update) và gọiProductCreateSyncService/ProductUpdateSyncService.syncToAdditionalMerchants().
EventBus tách rời phát khỏi enqueue; BullMQ cung cấp độ bền, retry, và concurrency có giới hạn (APP_ENV_BULLMQ_WORKER_CONCURRENCY).
Hệ quả
| Ưu | Nhược |
|---|---|
| Response aggregate chính nhanh — sao chép hoàn toàn async | Phát xảy ra sau commit; crash giữa commit và enqueue làm mất trigger sync |
| Lỗi theo từng merchant được cô lập, retry bởi BullMQ | Hai chặng infra (EventBus → hàng đợi Redis) phải suy luận |
| Bước onboarding + sync dùng chung một sự kiện, đăng ký hai lần | EventBus chỉ in-process — không phân phối cross-replica |
| Concurrency worker điều chỉnh được; chỉ chạy ở vai trò worker | Nhất quán cuối cùng: merchant được sao chép trễ hơn bản chính |
Phương án đã cân nhắc
| Tùy chọn | Ưu | Nhược | Vì sao bị từ chối |
|---|---|---|---|
| Sao chép đồng bộ bên trong TX aggregate | Nhất quán mạnh | Response chậm; một merchant xấu rollback tất cả | UX + bán kính tác động không chấp nhận được |
| Kafka topic cho fan-out sync | Cross-replica, bền | Thêm đấu nối consumer; commerce chỉ producer theo ADR-0002 | Quá mức cho một job nội bộ, cùng dịch vụ |
| Enqueue BullMQ trực tiếp từ service (không EventBus) | Ít chặng hơn | Gắn service với hàng đợi; side-effect bước onboarding cần đường thứ hai | EventBus đa hợp gọn sự kiện tới cả hai listener |
Tham khảo
src/common/event.ts(EVENT_EMITTER.PRODUCT.*)src/components/event-bus/component.ts(registry, đăng ký képcreated)src/events/listeners/product-aggregate-created.listener.ts/...updated.listener.tssrc/components/queues/queue.component.ts,src/components/workers/sync-product.worker.tssrc/services/product/product-create.service.ts:225,product-update.service.ts:228