Event-Driven Architecture: Kafka, RabbitMQ, và SignalR Trong Thực Tế
LinkedIn xử lý 7 triệu events mỗi giây qua Kafka. Uber gửi rider location updates 4 lần/giây cho hàng triệu users đồng thời. Netflix dùng event streaming để đồng bộ xem phim giữa 238 triệu tài khoản. Điểm chung? Event-Driven Architecture (EDA) — pattern mà hệ thống phản ứng với events thay vì chờ đợi request. Bài viết này giải thích EDA từ lý thuyết đến thực tế triển khai, và tại sao BanhCuonFlow chọn SignalR cho toàn bộ realtime features.
EDA Là Gì? So Sánh Với Request-Response
Kiến trúc truyền thống (request-response): Client gửi request → Server xử lý → Server trả response. Đơn giản, dễ hiểu, nhưng có 2 vấn đề: tight coupling (client phải biết server address) và blocking (client chờ response).
Event-Driven: Producer phát ra event ("task đã hoàn thành") → Multiple consumers nhận và xử lý độc lập: consumer A gửi email thông báo, consumer B cập nhật dashboard, consumer C trigger workflow tiếp theo. Producer không biết cũng không cần biết ai sẽ xử lý event. Đây là loose coupling — services có thể thêm, bớt, scale mà không ảnh hưởng lẫn nhau.
So Sánh Chi Tiết Message Brokers
🟠 Apache Kafka
Bản chất: Distributed commit log — events được lưu vĩnh viễn (configurable retention), consumers có thể replay từ bất kỳ thời điểm nào. Throughput: Triệu messages/giây, horizontal scaling bằng partitions. Ưu điểm: Durable, replayable, fault-tolerant. Phù hợp: event streaming, data pipeline, analytics. Nhược: Complex setup (Zookeeper/KRaft), learning curve cao, overkill cho ứng dụng nhỏ. Khi nào dùng: Team 50+ engineers, throughput > 100K events/sec, cần event replay.
🐰 RabbitMQ
Bản chất: Traditional message broker — messages được consume rồi xóa (acknowledgment model). Routing: Flexible exchanges (direct, fanout, topic, headers) — route messages dựa trên routing keys. Ưu điểm: Dễ setup, documentation tốt, plugins phong phú, hỗ trợ nhiều protocols (AMQP, MQTT, STOMP). Nhược: Throughput thấp hơn Kafka (50K msgs/sec), không replayable. Khi nào dùng: Task queues, microservice communication, RPC, team < 20 engineers.
🔵 SignalR (.NET)
Bản chất: WebSocket abstraction — realtime bi-directional communication giữa server và browser. Auto fallback: WebSocket → Server-Sent Events → Long Polling. Ưu điểm: Native .NET integration, zero config cho single-server, Groups/Hubs pattern dễ dùng. Nhược: Chỉ cho browser↔server (không phải service↔service), scale multi-server cần Redis backplane. Khi nào dùng: Chat, notifications, live dashboards, collaboration features.
3 Patterns Quan Trọng
📡 Pub/Sub (Publish-Subscribe)
Publisher gửi event đến topic/channel. Tất cả subscribers nhận bản copy. Loose coupling tối đa. BanhCuonFlow dùng Pub/Sub qua SignalR Groups: mỗi chat room là 1 group, khi user gửi tin nhắn → broadcast đến tất cả members của group đó. Thêm member → tự nhận tin nhắn mới. Rời group → ngừng nhận. Không cần sửa code publisher.
📜 Event Sourcing
Thay vì lưu state hiện tại (balance: $100), lưu tất cả events dẫn đến state đó: deposited $50, deposited $70, withdrew $20. Rebuild state bất kỳ lúc nào bằng replay. Ưu điểm: Full audit trail, time-travel debugging, undo/redo. Nhược: Phức tạp, eventually consistent, event schema migration đau đầu.
🔀 CQRS (Command Query Responsibility Segregation)
Tách write model (commands: create, update, delete) và read model (queries: search, list, report). Write model optimize cho business logic integrity. Read model optimize cho query speed (denormalized, materialized views). Phù hợp: Hệ thống read-heavy (90% reads, 10% writes) hoặc cần report phức tạp.
EDA Trong BanhCuonFlow: Thực Tế Triển Khai
BanhCuonFlow sử dụng SignalR làm event transport chính cho toàn bộ realtime features:
💬 Chat Realtime
SignalR Groups = chat rooms. Broadcast tin nhắn, typing indicator, read receipts. Redis backplane cho multi-server.
🔔 Notifications
Task assigned → push notification realtime đến user đang online. User offline → queue trong DB, hiển thị khi login.
📊 Live Dashboards
Khi task status thay đổi → broadcast update đến dashboard. Manager thấy KPIs cập nhật realtime mà không cần refresh.
⚙️ Workflow Triggers
Task chuyển trạng thái → trigger lifecycle hooks: gửi email, tạo sub-task, gọi webhook. All async, non-blocking.
Pitfalls Khi Triển Khai EDA
① Event ordering: Events có thể đến không theo thứ tự — đặc biệt khi có multiple producers hoặc network delays. Giải pháp: partition by entity ID (Kafka) hoặc sequence number trong event payload. BanhCuonFlow: SignalR messages ordered per-connection naturally, cross-server ordering via Redis sorted sets.
② Idempotency: Consumer có thể nhận cùng 1 event 2 lần (network retry, consumer restart). Mọi handler phải idempotent — xử lý 1 lần hay 10 lần đều cho kết quả giống nhau. BanhCuonFlow dùng event ID + deduplication: mỗi event có GUID unique, handler check trước khi xử lý.
③ Debugging difficulty: Khi có bug, trace flow qua 5 services asynchronous khó hơn trace 1 API call đồng bộ. Giải pháp: correlation ID gắn vào mọi event (propagate từ original request), structured logging (JSON format, Seq/ELK), distributed tracing (OpenTelemetry + Jaeger).
④ Event schema evolution: Khi event schema thay đổi (thêm field, rename field), cần backward compatibility. Consumer cũ phải đọc events mới mà không crash. Strategies: optional fields with defaults, schema registry (Avro/Protobuf), version number trong event header. BanhCuonFlow: JSON events với optional fields — backward compatible by design.
⑤ Eventual consistency: Trong EDA, data consistency là eventually consistent — không phải strong consistency. User tạo task xong, dashboard có thể chưa cập nhật ngay (50-200ms delay). Đối với BanhCuonFlow: SignalR broadcast sub-100ms, users hầu như không nhận ra delay. Nhưng cho critical operations (financial, approval), cần synchronous confirmation trước khi fire-and-forget events.