ADR-0001. EventBus trong tiến trình + 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 | Phat Nguyen |
| 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 sang các merchant khác của cùng organizer (chuỗi cửa hàng dùng chung catalog).
- Việc tạo/cập nhật aggregate vốn đã là một transaction DB duy nhất (Product + Info + identifiers + liên kết kênh + variant mặc định). Việc sao chép tác động đến nhiều hàng trên N merchant và có thể chậm.
- Nó không được chặn hoặc làm hỏng phản hồi của aggregate chính, và phải trụ được qua lỗi cục bộ (bản sao của một merchant bị lỗi không được làm rollback bản chính).
- Commerce vốn đã chạy sẵn một kết nối BullMQ Redis và một vai trò worker.
Quyết định
Tách việc sao chép thành hai giai đoạn nối với nhau bằng một EventBus trong tiến trình (eventemitter3) và một queue BullMQ:
ProductCreateService/ProductUpdateServicecommit aggregate chính, rồi phátproduct.aggregate.{created,updated}trên EventBus (các khóaEVENT_EMITTERtrongsrc/common/event.ts).ProductAggregateCreatedListener/ProductAggregateUpdatedListenerxử lý sự kiện. Listenercreatedcòn đánh dấu bước onboarding merchantPRODUCT. Cả hai đều gọipushJobToQueue()- nhưng chỉ khisyncMerchantIds.length > 0.- Job được đưa vào
SYNC_PRODUCT_QUEUE(@nx/commerce/sync-product-queue) trên BullMQ Redis. SyncProductWorkerxử lý job đó (tên job.createso với.update) và gọiProductCreateSyncService/ProductUpdateSyncService.syncToAdditionalMerchants().
EventBus tách việc phát sự kiện khỏi việc đưa vào queue; BullMQ mang lại độ bền, khả năng thử lại và mức xử lý đồng thời có giới hạn (APP_ENV_BULLMQ_WORKER_CONCURRENCY).
Hệ quả
| Ưu | Nhược |
|---|---|
| Phản hồi của aggregate chính nhanh - việc sao chép hoàn toàn bất đồng bộ | Sự kiện được phát sau khi commit; nếu sập giữa lúc commit và lúc đưa vào queue thì mất trigger đồng bộ |
| Lỗi của từng merchant được cô lập, được BullMQ thử lại | Có hai chặng hạ tầng (EventBus → queue Redis) cần phải nắm |
| Bước onboarding và đồng bộ dùng chung một sự kiện, đăng ký hai lần | EventBus chỉ chạy trong tiến trình - không phân phối được giữa các bản sao |
| Mức xử lý đồng thời của worker tuỳ chỉnh được; chỉ chạy ở vai trò worker | Nhất quán sau cùng: các 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ộ ngay trong TX của aggregate | Nhất quán mạnh | Phản hồi chậm; một merchant lỗi làm rollback tất cả | Trải nghiệm người dùng và phạm vi ảnh hưởng không chấp nhận được |
| Dùng Kafka topic để phát tán việc đồng bộ | Vượt được nhiều bản sao, bền | Phải thêm phần đấu nối consumer; commerce chỉ là producer theo ADR-0002 | Quá mức cần thiết cho một job nội bộ, cùng dịch vụ |
| Đưa thẳng vào queue BullMQ từ service (không qua EventBus) | Ít chặng hơn | Gắn chặt service với queue; tác dụng phụ của bước onboarding cần thêm một đường riêng | EventBus phân phối sự kiện tới cả hai listener một cách gọn gàng |
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