ADR-0006: Dùng song song Kafka (event streaming) và BullMQ (job queue)
- Status: Accepted
- Date: ~2026-06-27 (backfilled 2026-07-25)
- Supersedes: —
- Superseded by: —
Bối cảnh
Kiến trúc microservices (ADR-0001) cần giao tiếp async cho 2 nhu cầu khác nhau:
- Phát sự kiện domain (booking created, payment completed...) để nhiều service khác nhau cùng lắng nghe, có thể replay/scale consumer độc lập.
- Xử lý job nội bộ có độ trễ/cần retry (gửi email, xử lý tác vụ nền) mà không cần nhiều consumer group khác nhau lắng nghe.
Dùng 1 công nghệ duy nhất cho cả 2 nhu cầu sẽ ép buộc trade-off không cần thiết (Kafka không có sẵn concept "job with delay/backoff" tiện lợi như BullMQ; BullMQ không phù hợp làm event bus cho nhiều consumer group độc lập theo kiểu pub/sub bền vững).
Quyết định
Tách rõ 2 công cụ theo đúng use case, đóng gói trong libs/messaging:
- Kafka (
libs/messaging/src/kafka, quakafkajs): event streaming xuyên service — topic định nghĩa tạilibs/shared/src/constants/kafka-topics.ts, dùngKafkaProducerService+@KafkaConsumerdecorator - BullMQ (
libs/messaging/src/bullmq, qua Redis): job queue nội bộ một service (retry, delay, backoff), quaQueueService - Cả hai đều có OpenTelemetry instrumentation riêng
(
@opentelemetry/instrumentation-kafkajs) để giữ tracing nhất quán (ADR-0004)
Lựa chọn khác đã cân nhắc
- Chỉ dùng Kafka cho cả job queue: phải tự xây retry/backoff/delay logic trên Kafka (không có sẵn), phức tạp hơn nhiều so với dùng BullMQ có sẵn — loại.
- Chỉ dùng BullMQ/Redis cho cả event streaming: mất khả năng replay event, không có consumer group độc lập bền theo kiểu Kafka, Redis không phải thiết kế cho durable log — loại.
- RabbitMQ thay cho cả hai: có thể làm cả pub/sub và job queue, nhưng đổi cả 2 công nghệ đã chọn để hợp nhất về 1 broker duy nhất không cần thiết ở quy mô hiện tại — không xét lại vì thiếu động lực thay đổi.
Hệ quả
- Được: mỗi công nghệ dùng đúng sở trường (Kafka: durable event log, nhiều consumer; BullMQ: retry/backoff/delay linh hoạt cho job nội bộ).
- Đánh đổi: vận hành 2 hệ thống message riêng (Kafka cluster + Redis cho
BullMQ) thay vì 1; dev cần biết khi nào dùng cái nào — cần convention rõ
trong
libs/messagingđể tránh dùng nhầm.
Liên kết
- Docs liên quan:
docs/ARCHITECTURE.md(mục Communication — Async planned) - ADR liên quan: ADR-0001