Sổ tay Kiến trúc Phần mềm
09 Phần II · Phong cách Chương 14

Event-Driven Architecture

Event-Driven Architecture (EDA)

Phong cách phân tán bất đồng bộ cho hệ mở rộng & hiệu năng cực cao: các bộ xử lý tách rời phản ứng với sự kiện. Hai topology — Broker (nhanh, không người điều khiển) và Mediator (kiểm soát luồng) — là lựa chọn cốt lõi.

01 EDA là gì?

Đa số ứng dụng theo mô hình request: một orchestrator (UI/API) đồng bộ & tất định điều hướng request tới các bộ xử lý. EDA lật ngược: các thành phần phản ứng với sự kiện một cách bất đồng bộ, tách rời nhau.

Mô hình request

  • Đồng bộ, tất định: "làm việc X ngay bây giờ".
  • Orchestrator điều phối tập trung.

Mô hình event

  • Bất đồng bộ: "việc X vừa xảy ra" — ai quan tâm thì phản ứng.
  • Bộ xử lý tách rời, không biết nhau.

EDA có thể đứng độc lập hoặc nhúng vào phong cách khác (event-driven microservices, event-driven microkernel…). Toàn bộ giao tiếp là bất đồng bộ.

02 Broker topology

Không có mediator trung tâm. Luồng tin nhắn lan theo kiểu chuỗi/phát sóng qua một message broker nhẹ (RabbitMQ, ActiveMQ…), thường dùng topic + pub-sub. Bốn thành phần: sự kiện khởi đầu, event broker, event processor, sự kiện xử lý (processing event).

flowchart LR
  IE["Initiating event
(PlaceOrder)"]:::ie --> BR(("Broker
topic")):::br BR --> P1["OrderPlacement"]:::ep P1 -->|"order-created"| BR2(("Broker")):::br BR2 --> P2["Payment"]:::ep BR2 --> P3["Inventory"]:::ep BR2 --> P4["Notification"]:::ep classDef ie fill:#f3e6c8,stroke:#9a6712,color:#1c1a14; classDef br fill:#14233b,stroke:#14233b,color:#f3ede0; classDef ep fill:#e7ddf2,stroke:#6a4ca8,color:#1c1a14;
Mỗi processor làm việc của mình rồi "quảng cáo" một processing event; ai quan tâm thì nghe & phản ứng — cho tới khi không ai quan tâm nữa. Không ai biết toàn bộ giao dịch xong khi nào.

Ưu điểm

  • Bộ xử lý tách rời cao.
  • Mở rộng, phản hồi, hiệu năng & chịu lỗi đều cao.

Nhược điểm

  • Không kiểm soát workflow; xử lý lỗi rất khó.
  • Không khởi động lại / phục hồi; dễ lệch nhất quán dữ liệu (Payment sập mà Inventory vẫn trừ kho).

03 Mediator topology

Một event mediator trung tâm quản lý & điều khiển workflow. Năm thành phần: sự kiện khởi đầu, event queue, event mediator, event channels, event processors. Mediator biết các bước, sinh ra các lệnh (command) gửi point-to-point; processor làm xong báo lại mediator (không quảng cáo ra ngoài).

flowchart LR
  IE["Initiating event"]:::ie --> Q["Event queue"]:::q --> M{{"Event Mediator"}}:::med
  M -->|"place-order (command)"| C1["OrderPlacement"]:::ep
  M -->|"apply-payment"| C2["Payment"]:::ep
  M -->|"update-inventory"| C3["Inventory"]:::ep
  C1 -. ack .-> M
  C2 -. ack .-> M
  C3 -. ack .-> M
  classDef ie fill:#f3e6c8,stroke:#9a6712,color:#1c1a14;
  classDef q fill:#e2edf3,stroke:#2f6d93,color:#1c1a14;
  classDef med fill:#14233b,stroke:#14233b,color:#f3ede0;
  classDef ep fill:#e7ddf2,stroke:#6a4ca8,color:#1c1a14;
            
Điểm khác cốt lõi: broker phát event ("đã xảy ra", có thể bị bỏ qua); mediator gửi command ("cần xảy ra", bắt buộc xử lý). Thường có nhiều mediator theo domain để giảm điểm lỗi đơn.

Ưu điểm

  • Kiểm soát workflow, xử lý lỗi, phục hồi & khởi động lại tốt.
  • Quản lý trạng thái & nhất quán dữ liệu tốt hơn.

Nhược điểm

  • Processor ghép nối vào mediator; mediator dễ thành nút thắt.
  • Khó mô hình hoá workflow động phức tạp; mở rộng/hiệu năng thấp hơn broker.
Tiêu chíBrokerMediator
Điều khiển luồngKhông — phân tán, độngCó — tập trung, tất định
Loại "processing"Event ("đã xảy ra")Command ("cần xảy ra")
Xử lý lỗi & phục hồiKhóTốt
Hiệu năng & mở rộngCao nhấtTốt, nhưng thấp hơn

Chốt đánh đổi: chọn Broker hay Mediator chủ yếu là cân giữa kiểm soát luồng & xử lý lỗi (Mediator) với hiệu năng & mở rộng tối đa (Broker).

04 Bất đồng bộ, lỗi & chống mất dữ liệu

Hai kiểu giao tiếp bất đồng bộ

Fire-and-forget

không cần phản hồi

Gửi rồi quên. Tăng responsiveness: báo người dùng "đã nhận, sẽ xử lý" thay vì bắt chờ.

Request-reply

giả đồng bộ

Gửi vào request queue rồi blocking wait trên reply queue; khớp phản hồi bằng Correlation ID = message ID gốc.

Responsiveness ≠ Performance. Đăng bình luận mất 3.000ms xử lý: gọi đồng bộ → user chờ 3.100ms; gửi bất đồng bộ → user thấy "xong" sau 25ms (việc vẫn chạy nền). Async cải thiện phản hồi, không phải tốc độ xử lý thật.

Workflow event pattern (xử lý lỗi)

Khi consumer gặp lỗi, nó uỷ quyền ngay cho một workflow processor (error handler) rồi đọc tiếp tin nhắn sau — không để cả hàng đợi nghẽn vì một tin lỗi. Workflow processor cố sửa & gửi lại queue gốc; không sửa được thì đẩy lên "dashboard" cho người xử lý tay.

Ba kỹ thuật chống mất tin nhắn

Sync send

gửi đồng bộ

Producer đợi broker xác nhận đã lưu — tin không lạc giữa producer & queue.

Persisted queue

hàng đợi bền vững

Broker lưu tin ra đĩa (file/DB) → không mất khi hệ sập.

Client acknowledge

xác nhận của client

Tin chỉ rời queue khi consumer xác nhận đã xử lý & commit DB (LPS) — processor sập thì tin vẫn còn.

05 Rating & hệ RAG

EDA mạnh tuyệt đối ở hiệu năng, mở rộng, chịu lỗi, tiến hoá — nhưng rất phức tạp & khó test.

Hiệu suất Performance
Khả năng mở rộng Scalability
Khả năng chịu lỗi Fault tolerance
Khả năng tiến hóa Evolutionary
Tính mô-đun Modularity
Khả năng triển khai Deployability
Tính đàn hồi Elasticity
Độ tin cậy Reliability
Sự linh hoạt Agility
Chi phí tổng thể Overall cost
Khả năng kiểm thử Testability
Tính đơn giản Simplicity

Ingest RAG theo Broker

RAG  Nạp tài liệu rất hợp Broker: upload phát một sự kiện, các processor lần lượt phản ứng — tách rời, dễ thêm bước mới (vd "trích entity") mà không sửa cái cũ.

flowchart LR
  UP["document-uploaded"]:::ie --> B(("Broker")):::br
  B --> CH["Chunker"]:::ep
  CH -->|"doc-chunked"| B2(("Broker")):::br
  B2 --> EM["Embedder"]:::ep
  B2 --> EX["Trích entity (thêm sau)"]:::ep
  EM -->|"chunk-embedded"| B3(("Broker")):::br --> IX["Indexer → Vector Store"]:::ep
  classDef ie fill:#f3e6c8,stroke:#9a6712,color:#1c1a14;
  classDef br fill:#14233b,stroke:#14233b,color:#f3ede0;
  classDef ep fill:#e7ddf2,stroke:#6a4ca8,color:#1c1a14;
            
Thêm "Trích entity" chỉ là subscribe thêm vào doc-chunked — đúng điểm mạnh evolutionary 5★ của EDA.
PHP · fire-and-forget khi upload (responsiveness)
// Đẩy sự kiện rồi trả lời NGAY — không bắt người dùng chờ embed
$bus->publish('document-uploaded', ['doc_id' => $id]);
return response()->json(['status' => 'accepted'], 202);  // 25ms, không phải 3000ms
Cần kiểm soát chặt luồng (vd ingest có bước duyệt/khôi phục)? → đổi sang Mediator; chấp nhận mất chút hiệu năng để lấy điều khiển & phục hồi.

06 Ghi nhớ nhanh

EDA = phân tán, bất đồng bộ, tách rời — phản ứng với sự kiện; mở rộng & hiệu năng đỉnh, nhưng phức tạp & khó test.

Broker = không người điều khiển — pub-sub, processor quảng cáo event; nhanh & tách rời nhưng khó kiểm soát luồng/lỗi.

Mediator = có nhạc trưởng — gửi command, biết các bước; kiểm soát & phục hồi tốt, đổi lấy ghép nối & hiệu năng thấp hơn.

Responsiveness ≠ Performance — async làm hệ "phản hồi nhanh", không rút ngắn thời gian xử lý thật.

Chống mất tin — sync send + persisted queue + client acknowledge; lỗi thì dùng workflow event pattern (uỷ quyền cho error handler).

NguồnChương 14 (Event-Driven Architecture Style), Fundamentals of Software Architecture — Mark Richards & Neal Ford, O'Reilly 2020.