Skip to content

ADR-0001. EventBus trong tiến trình + BullMQ cho đồng bộ sản phẩm đa merchant

TrườngGiá trị
StatusAccepted
Ngày2026-03-01
Người quyết địnhPhat 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.
  • 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:

  1. ProductCreateService / ProductUpdateService commit aggregate chính, rồi phát product.aggregate.{created,updated} trên EventBus (các khóa EVENT_EMITTER trong src/common/event.ts).
  2. ProductAggregateCreatedListener / ProductAggregateUpdatedListener xử lý sự kiện. Listener created còn đánh dấu bước onboarding merchant PRODUCT. Cả hai đều gọi pushJobToQueue() - nhưng chỉ khi syncMerchantIds.length > 0.
  3. Job được đưa vào SYNC_PRODUCT_QUEUE (@nx/commerce/sync-product-queue) trên BullMQ Redis.
  4. SyncProductWorker xử lý job đó (tên job .create so với .update) và gọi ProductCreateSyncService / 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ả

ƯuNhượ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ạiCó 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ầnEventBus 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ò workerNhấ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ƯuNhượcVì sao bị từ chối
Sao chép đồng bộ ngay trong TX của aggregateNhất quán mạnhPhả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ềnPhải thêm phần đấu nối consumer; commerce chỉ là producer theo ADR-0002Quá 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ơnGắ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êngEventBus 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ép created)
  • src/events/listeners/product-aggregate-created.listener.ts / ...updated.listener.ts
  • src/components/queues/queue.component.ts, src/components/workers/sync-product.worker.ts
  • src/services/product/product-create.service.ts:225, product-update.service.ts:228

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.