Trong các hệ thống xây dựng trên Azure, ba dịch vụ messaging thường khiến developer dễ nhầm nhất là:

Azure Event Grid
Azure Service Bus
Azure Event Hubs

Cả ba đều liên quan đến message/event, nhưng mục đích sử dụng rất khác nhau.

Nếu chọn sai service, hệ thống có thể vẫn chạy nhưng sẽ gặp vấn đề khi scale, retry, xử lý duplicate, đảm bảo ordering hoặc vận hành production.

Bài viết này tập trung vào Azure Event Grid, cách nó hoạt động, những điểm cần lưu ý trong production, và cách phân biệt với Service Bus và Event Hubs.


1. Azure Event Grid là gì?

Azure Event Grid là một fully managed publish-subscribe event distribution service.

Có thể hiểu đơn giản:

Something happened
        ↓
   Event Grid
        ↓
Notify subscribers

Ví dụ:

BlobCreated
OrderCompleted
UserRegistered
SubscriptionExpired

Publisher chỉ thông báo:

Một sự kiện đã xảy ra.

Publisher không cần biết consumer nào sẽ xử lý event đó.

Ví dụ:

Order Service
     ↓
OrderCompleted
     ↓
Event Grid
     ├── Notification Service
     ├── Analytics Service
     └── Audit Service

Đây là mô hình publish/subscribe.


2. Event không giống Command

Đây là distinction quan trọng khi thiết kế event-driven system.

Event

Event mô tả một sự thật đã xảy ra.

Ví dụ:

OrderCompleted
UserCheckedOut
BlobCreated
RewardGranted

Ý nghĩa:

Something happened.

Publisher không yêu cầu cụ thể một consumer phải làm gì.


Command

Command yêu cầu một hành động được thực hiện.

Ví dụ:

ProcessPayment
GrantReward
GenerateInvoice
SendNotification

Ý nghĩa:

Please do this.

Command thường có expectation rõ ràng rằng operation phải được xử lý.

Đây là một cách đơn giản để phân biệt Event Grid và Service Bus:

Event Grid
→ Something happened.

Service Bus
→ Please do this.

3. Các thành phần chính của Event Grid

Một flow Event Grid cơ bản:

Event Source
    ↓
Topic
    ↓
Event Subscription
    ↓
Event Handler

Event Source

Event Source là nơi phát sinh event.

Ví dụ:

Azure Blob Storage
Custom .NET Application
Azure Resource Manager
Event Hubs

Topic

Topic là nơi publisher publish event.

Có thể hình dung:

Producer
   ↓
 Topic

Các subscriber đăng ký nhận event từ topic.

Event Grid có nhiều loại topic, trong đó phổ biến là:

System Topic
Custom Topic
Namespace Topic

System Topic

Đại diện cho events đến từ Azure service.

Ví dụ:

Azure Storage
    ↓
BlobCreated
    ↓
System Topic

Custom Topic

Dùng khi application tự publish business event.

Ví dụ:

Order Service
    ↓
OrderCompleted
    ↓
Custom Topic

4. Event Subscription

Event Subscription định nghĩa:

Which events?
+
Where should they go?

Ví dụ:

OrderEvents Topic

Có:

Subscription A
OrderCompleted
→ Notification

Subscription B
OrderCompleted
→ Analytics

Subscription C
OrderCancelled
→ Customer Support

Publisher không cần biết có bao nhiêu subscription tồn tại.

Đây chính là loose coupling.


5. Event Filtering

Không phải subscriber nào cũng cần nhận tất cả event.

Giả sử topic có:

OrderCreated
OrderPaid
OrderCancelled
OrderCompleted

Notification service chỉ quan tâm:

OrderPaid
OrderCompleted

Event Grid có thể filter ngay trước khi delivery.

Có thể filter dựa trên:

Event type
Subject
Prefix / suffix
Event attributes
Event data

Ví dụ Blob Storage:

/images/a.jpg
/documents/a.pdf
/images/b.jpg

Có thể chỉ subscribe:

subjectEndsWith = ".jpg"

Một nguyên tắc tốt:

Nếu broker có thể filter event thì nên filter ở broker thay vì gửi toàn bộ event xuống consumer rồi mới bỏ đi.


6. Event Schema

Khi publish event, cần có một schema rõ ràng.

Một cách phổ biến hiện nay là sử dụng CloudEvents.

Ví dụ:

{
  "specversion": "1.0",
  "id": "event-123",
  "type": "attendance.checked_out",
  "source": "/attendance",
  "subject": "/attendance/A123",
  "time": "2026-09-11T03:00:00Z",
  "data": {
    "attendanceId": "A123",
    "userId": "U100"
  }
}

Có thể chia thành:

Event metadata

id
type
source
subject
time

và:

Business payload

data

Không nên nhét toàn bộ business information vào tên topic hoặc event name.


7. Delivery Guarantee

Một điểm cực kỳ quan trọng:

Event Grid là at-least-once delivery.

Điều này có nghĩa:

Event A
→ có thể được nhận 1 lần

hoặc:

Event A
→ có thể được nhận nhiều lần

Không được assume rằng mỗi event chỉ xuất hiện đúng một lần.


8. Tại sao Consumer phải Idempotent?

Giả sử event:

UserCheckedOut

consumer xử lý:

Reward += 100

Nếu event được delivery hai lần:

Reward += 100

Reward += 100

User nhận 200 điểm.

Đó là bug.

Consumer phải thiết kế idempotent.

Ví dụ:

processed_events

event_id
processed_at

với:

UNIQUE(event_id)

Flow:

Receive Event
     ↓
Already processed?
     │
     ├── Yes
     │    ↓
     │   Ignore safely
     │
     └── No
          ↓
      Process business logic
          ↓
      Mark processed

Một giải pháp mạnh hơn là lưu trạng thái processed cùng business operation trong một transaction.

Ví dụ:

BEGIN

INSERT processed_events(event_id)

CREATE reward

COMMIT

Nếu duplicate event đến:

INSERT event_id
→ already exists

consumer có thể ignore an toàn.


9. Event Grid không đảm bảo Ordering

Không nên assume:

Event 1
Event 2
Event 3

sẽ luôn đến:

1
2
3

Nếu business phụ thuộc strict ordering:

CheckIn
→ CheckOut

thì cần cân nhắc kỹ.

Có thể cần:

sequence number
version
state validation

hoặc dùng một messaging solution có ordering guarantee mạnh hơn.

Ví dụ Service Bus Sessions.


10. Retry

Nếu consumer xử lý thất bại:

Event Grid
    ↓
Consumer
    ↓
HTTP 503

Event Grid có thể retry.

Concept:

Delivery
   ↓
Failure
   ↓
Retry
   ↓
Retry
   ↓
Retry

Tuy nhiên cần phân biệt transient error và permanent error.

Transient errors

Ví dụ:

Timeout
503 Service Unavailable
Temporary network issue

Retry có ý nghĩa.

Permanent errors

Ví dụ:

Invalid payload
Unauthorized
Business validation failed permanently

Retry nhiều lần thường không giúp ích.

Consumer vẫn cần được thiết kế để hiểu loại lỗi nào có thể retry.


11. Dead Letter

Nếu Event Grid retry nhiều lần nhưng vẫn không delivery được:

Event Grid
    ↓
Retry
    ↓
Retry
    ↓
Still failed
    ↓
Dead Letter

Dead Letter không nên bị xem như:

message failed
→ done

Production flow phải là:

Dead Letter
    ↓
Alert
    ↓
Investigate
    ↓
Fix root cause
    ↓
Replay / Manual processing

Một Senior Engineer cần nghĩ thêm:

Ai monitor DLQ?

Bao lâu thì alert?

Replay bằng cách nào?

Replay có gây duplicate không?

Consumer có idempotent không?

12. Push và Pull Delivery

Event Grid có thể hoạt động theo nhiều cách.

Push

Event Grid chủ động gửi event tới consumer.

Event Grid
    ↓ HTTP
Consumer

Phù hợp với:

Azure Function
Webhook
Reactive processing

Pull

Consumer chủ động lấy event.

Consumer
    ↓
Receive events
    ↓
Event Grid

Pull phù hợp khi consumer cần kiểm soát:

processing rate
backpressure
batching
worker capacity

Nó gần với queue-consumer model hơn.


13. Event Grid vs Service Bus vs Event Hubs

Đây là phần quan trọng nhất khi làm Azure architecture.

Event Grid

Mental model:

Something happened.

Phù hợp với:

Event notification
Event routing
Fan-out
Reactive architecture

Ví dụ:

BlobCreated
OrderCompleted
UserRegistered

Service Bus

Mental model:

Please do this.

Phù hợp với:

Business commands
Work queue
Transactional messaging
Reliable processing

Ví dụ:

ProcessPayment
GenerateInvoice
GrantReward

Service Bus có các capability mạnh hơn cho enterprise messaging như:

Queues
Topics
Dead-letter
Sessions
Duplicate detection
Transactions

Event Hubs

Mental model:

Here is a continuous stream of data.

Phù hợp với:

Telemetry
Logs
IoT
Clickstream
High-throughput data ingestion

Ví dụ:

1M device events / second

14. Một bảng so sánh nhanh

Event Grid Service Bus Event Hubs
Purpose Event routing Reliable messaging Event streaming
Mental model Something happened Do this work Stream data
Pub/Sub Yes Topics Consumer Groups
Ordering Không guarantee Có thể dùng Sessions Per partition
Transactions Không phải trọng tâm Không phải trọng tâm
Duplicate detection Consumer xử lý Có support Consumer xử lý
Replay Không phải use case chính Không phải streaming replay
Typical use BlobCreated ProcessPayment Telemetry

15. Khi nào dùng Event Grid?

Event Grid phù hợp khi:

Một sự kiện đã xảy ra

và nhiều hệ thống có thể quan tâm.

Ví dụ:

UserRegistered
     ↓
Event Grid
     ├── Welcome Email
     ├── Analytics
     └── Audit

Hoặc:

BlobCreated
     ↓
Event Grid
     ├── Image Processor
     └── Metadata Processor

16. Khi nào không nên dùng Event Grid?

Không nên ép Event Grid vào mọi messaging problem.

Ví dụ:

ProcessPayment

là một critical business operation.

Bạn có thể cần:

Retry
Dead Letter
Ordering
Transaction
Duplicate Detection
Controlled consumption

Trong trường hợp đó, Service Bus có thể phù hợp hơn.

Một nguyên tắc:

Không chọn broker dựa trên việc service nào quen dùng, mà dựa trên semantics của business operation.


17. Có thể dùng Event Grid và Service Bus cùng nhau

Không cần chọn chỉ một.

Ví dụ:

Attendance Service
      ↓
Outbox
      ↓
Service Bus
      ↓
Reward Worker

Reward processing là critical business operation.

Sau khi thành công:

RewardGranted
     ↓
Event Grid
     ├── Notification
     ├── Analytics
     └── Audit

Ở đây:

Service Bus
→ xử lý business work đáng tin cậy

Event Grid
→ broadcast business fact

Hai service bổ sung cho nhau.


18. Security

Trong production Azure, tránh hard-code:

connection string
access key
secret

Khi có thể, nên sử dụng:

Managed Identity
+
Azure RBAC

Ví dụ:

Event Grid
   ↓
Managed Identity
   ↓
RBAC permission
   ↓
Service Bus

Điều này giúp giảm:

secret management
secret rotation
credential leakage

19. Observability cho Event Grid

Event-driven system rất khó debug nếu không có traceability.

Một event nên có các metadata hữu ích:

event_id
correlation_id
event_type
message_id
aggregate_id

Ví dụ:

{
  "event_id": "EVT100",
  "correlation_id": "TRACE001",
  "event_type": "AttendanceCheckedOut",
  "attendance_id": "ATT123"
}

Một E2E flow:

API
 ↓
Event Grid
 ↓
Function
 ↓
Service Bus
 ↓
Worker

nên có khả năng trace được toàn bộ bằng:

TraceId
CorrelationId
EventId

Metrics nên theo dõi:

events_published
delivery_failures
dead_letter_count
processing_duration
consumer_failure_rate

Nếu:

dead_letter_count > 0

nên có alert.


20. Failure Scenario thực tế

Giả sử consumer xử lý:

OrderCompleted

Flow:

Receive event
   ↓
Create invoice
   ↓
DB COMMIT
   ↓
Application crashes

Consumer chưa trả success.

Event Grid có thể retry.

Consumer nhận lại:

OrderCompleted

Nếu không idempotent:

Invoice #1
Invoice #2

Sai.

Nếu có Inbox / ProcessedEvent:

BEGIN

INSERT processed_event(event_id)

CREATE invoice

COMMIT

duplicate delivery:

event_id already exists

=> consumer biết event đã được xử lý.

Đây là lý do:

Retry và Idempotency luôn phải được thiết kế cùng nhau.


21. Cách trả lời khi phỏng vấn

Nếu interviewer hỏi:

When would you use Event Grid instead of Service Bus?

Có thể trả lời:

I use Event Grid when I need to distribute discrete business or infrastructure events to multiple independent subscribers. The publisher only communicates that something happened and does not need to know who consumes the event.

For business commands that must be reliably processed, such as payment or reward processing, I normally prefer Service Bus because it provides stronger messaging capabilities for transactional workloads.

I also assume Event Grid delivery is at least once and unordered, so consumers must be idempotent and should not depend on delivery order.


22. Mental Model dễ nhớ

Có thể nhớ ba Azure services bằng ba câu:

Event Grid
→ Something happened.

Service Bus
→ Please do this.

Event Hubs
→ Here is a stream of data.

Và riêng Event Grid:

Publish Event
     ↓
Event Grid
     ↓
Filter
     ↓
Fan-out
     ↓
Subscribers

Nhưng khi triển khai production phải bổ sung:

At-least-once
       ↓
Idempotency

No ordering guarantee
       ↓
Don't depend on arrival order

Delivery failure
       ↓
Retry + Dead Letter

Production operation
       ↓
Metrics + Logs + Tracing

Kết luận

Azure Event Grid không đơn giản chỉ là một service để gửi message.

Nó phù hợp với kiến trúc:

Event-driven
Publish / Subscribe
Loose coupling
Reactive systems

Điểm quan trọng nhất khi thiết kế là hiểu semantics của event.

Nếu message mang nghĩa:

Something happened

Event Grid là một candidate rất tốt.

Nếu message mang nghĩa:

Please perform this critical work

Service Bus thường phù hợp hơn.

Nếu dữ liệu là:

A continuous high-volume stream

hãy nghĩ tới Event Hubs.

Cuối cùng, dù sử dụng service nào, production architecture vẫn cần xem xét:

Delivery guarantee
Retry
Duplicate
Idempotency
Ordering
Dead Letter
Security
Observability
Recovery

Đó mới là phần quyết định một event-driven system có thực sự đáng tin cậy hay không.