Sau queue vs stream (channel làm được gì), hỏi tiếp: channel dùng để làm gì? Cùng một Kafka topic, recovery “đúng” cho payment worker có thể phá projection — và ngược lại.

Ba purpose: write, read, shared. Demo: Đổi purpose → strategy hợp / không hợp.

Write side — business phải chạy

Channel kích hoạt hành động: trừ tiền, gửi mail welcome, mở ticket 3rd-party, bước workflow… Message = “hãy làm việc này”.

  • Ưu tiên: hoàn thành action, continuity. Một payment fail tạm không nên đóng băng mọi payment sau trên cùng lane nếu business không yêu cầu.
  • Order tuyệt đối thường không phải requirement toàn cục — hoặc chỉ per aggregate (account id) qua partition key.
  • Fail strategy hay gặp: instant retry → delayed resend / error queue → DLQ — nhằm unblock + tự heal transient.

Viết tắt: write side dừng channel = dừng một phần business.

Read side — dựng view từ lịch sử

Projection, search index, analytics, sync read model: “xảy ra gì thì phản ánh vào view”.

  • Ưu tiên: đủ event + thứ tự có nghĩa (thường per-key). Skip / ignore bừa = view sai hoặc thiếu, không chỉ “chậm”.
  • Fail / block projection: user thấy data cũ — stale — business write vẫn chạy được. Đó là trade-off chấp nhận được hơn corrupt view.
  • Strategy: instant retry OK; release/rewind giữ order; cẩn thận resend phá order; ignore gần như cấm nếu view phải đúng.

Viết tắt: read side chậm được, sai không được (trong phạm vi consistency bạn đã hứa).

Shared — nhiều group / app cùng channel

Stream với ≥2 consumer group (billing + analytics + audit…), hoặc bất kỳ setup nào message được consume độc lập nhiều lần theo group.

  • App A fail ≠ app B fail. B có thể đã xử lý xong.
  • Resend message gốc vào channel → B có thể nhận lần hai (trừ khi mọi consumer idempotent tuyệt đối).
  • An toàn hơn: error channel / DLQ riêng group A, không đụng shared log theo kiểu nhân bản event cho cả thế giới.

Chi tiết: resend single-group vs shared.

Ma trận purpose × ưu tiên

              Unblock flow    Giữ order/đủ event    Đừng double app khác
Write            ★★★               ★                 ★★ (nếu shared)
Read             ★                 ★★★               ★★
Shared           ★★                ★                 ★★★

★★★ = constraint cứng. Strategy nào đụng ★★★ là “sai purpose”.

Ví dụ chọn nhanh

  • orders.commands / queue worker “charge card” → write, single app → delayed retry + DLQ, resend OK hơn là release mãi một poison.
  • orders.events → projection balance → read → đừng ignore; poison thì park + alert, cân nhắc không xáo order per account.
  • Topic dùng chung CRM + warehouse → shared → fail CRM không produce lại same event lên topic chung.

Queue “không shared” mặc định

Classic queue thường hành xử như một message group: ai lấy thì của người đó. Shared theo nghĩa nhiều app độc lập ít gặp hơn stream — trừ khi bạn fan-out exchange → nhiều queue (mỗi app một queue = không còn một channel shared, mà nhiều subscription).

Một câu để nhớ

Write: đừng chặn business. Read: đừng làm view sai. Shared: đừng recovery theo cách nhân bản cho hàng xóm.

Purpose × loại channel → mở playbook.