Bối cảnh

System event dùng để gọi một sự kiện ở hệ thống khác thông qua HTTP. Thay vì mỗi module tự biết danh sách endpoint đích và tự gửi request, hệ thống dùng một internal event trung gian có tên:
ezy_system_event
Trong code, tên này tương ứng với hằng:
INTERNAL_EVENT_NAME_EZY_SYSTEM_EVENT
Internal event này đóng vai trò như một “cổng chuyển tiếp”: module nghiệp vụ chỉ cần phát event nội bộ, còn plugin GraphQL admin sẽ nhận event đó và gửi HTTP POST tới các system endpoint đã cấu hình.

Kiến trúc tổng quát

sequenceDiagram
    participant Business as Module nghiệp vụ
    participant Internal as EventHandlerManager
    participant Relay as GraphQL system event relay
    participant Endpoint as System endpoint
    participant Target as Event handler đích

    Business->>Internal: handleEvent("ezy_system_event", payload)
    Internal->>Relay: gọi handler của internal event
    Relay->>Endpoint: POST payload + Bearer token
    Endpoint->>Endpoint: kiểm tra eventName có được phép xử lý
    Endpoint->>Target: handleEvent(eventName, data)
    Target-->>Endpoint: result hoặc no content

Payload chuẩn

Khi muốn gọi một system event thông qua internal event, payload cần có dạng:
{
  "eventName": "ten_system_event_can_goi",
  "data": {
    "field1": "value1",
    "field2": "value2"
  }
}
Trong đó:
  • eventName: tên system event thật sự muốn gọi ở hệ thống nhận.
  • data: dữ liệu truyền cho event handler đích.
  • Internal event bên ngoài luôn là ezy_system_event.
Ví dụ từ ecommerce, khi cần phát system event thông báo đơn hàng của user, module phát internal event như sau:
eventHandlerManager.handleEvent(
    INTERNAL_EVENT_NAME_EZY_SYSTEM_EVENT,
    mapBuilder()
        .put("eventName", SYSTEM_EVENT_NAME_USER_ORDER_NOTIFICATION)
        .put("data", mapBuilder()
            .put("orderId", orderId)
            .put("username", username)
            .put("userDisplayName", userDisplayName)
            .put("userEmail", userEmail)
            .put("userPhoneNumber", userPhoneNumber)
            .put("orderProducts", orderProducts)
            .put("userRequestedDomain", userRequestedDomain)
            .toMap()
        )
        .toMap()
);
Lưu ý: với ecommerce hiện tại, tên system event cụ thể đang là:
ezy_sytem_event_user_order_notification
Hãy dùng đúng chuỗi đang được khai báo trong hệ thống, kể cả khi tên có vẻ bị thiếu chữ.

Cách cấu hình bên gửi

Bên gửi cần có GraphQL admin plugin để làm relay cho internal event ezy_system_event.
Khi relay nhận được internal event, nó sẽ:
  1. Lấy danh sách system endpoint có loại SYSTEM_ENDPOINT.
  2. Chỉ lấy endpoint đang ở trạng thái VISIBLE.
  3. Bỏ qua endpoint không có token.
  4. Gửi HTTP POST tới từng endpoint.
  5. Thêm header:
Authorization: Bearer <token>
  1. Body request chính là payload của internal event.
Ví dụ body được gửi đi:
{
  "eventName": "ezy_sytem_event_user_order_notification",
  "data": {
    "orderId": 1,
    "username": "john",
    "userDisplayName": "John",
    "userEmail": "john@example.com",
    "userPhoneNumber": "0900000000",
    "orderProducts": [
      {
        "productCode": "P001",
        "quantity": 1,
        "price": 100000
      }
    ],
    "userRequestedDomain": "example.com"
  }
}

Cách cấu hình bên nhận

Bên nhận cần expose API nhận system event, hiện tại là:
POST /api/v1/system-event
API này nhận body:
{
  "eventName": "ten_system_event",
  "data": {}
}
Trước khi gọi event handler thật, hệ thống kiểm tra eventName có nằm trong danh sách system event được phép xử lý hay không.
Danh sách này được cấu hình bằng setting:
allowing_handling_system_event_names
Nếu eventName không nằm trong danh sách cho phép, request sẽ bị từ chối với lỗi forbidden.

Ví dụ với ecommerce

Luồng thông báo đơn hàng trong ecommerce hoạt động như sau:
  1. Khi có dữ liệu thông báo đơn hàng, ecommerce phát internal event:
user_order_notification_append
  1. Một handler trong ecommerce nhận event này.
  2. Nếu setting cho phép gửi system event đơn hàng đang bật, handler tiếp tục phát internal event:
ezy_system_event
  1. Payload bên trong có eventName là system event thông báo đơn hàng.
  2. GraphQL relay nhận ezy_system_event và gửi POST tới các system endpoint đã cấu hình.
  3. Hệ thống nhận kiểm tra whitelist rồi gọi handler tương ứng với system event đích.

Điều kiện để event chạy thành công

Để gọi system event qua internal event thành công, cần đủ các điều kiện sau:
  • Bên gửi có handler cho internal event ezy_system_event.
  • Có ít nhất một system endpoint đang VISIBLE.
  • System endpoint có bearer token hợp lệ.
  • Bên nhận cho phép xử lý eventName trong setting allowing_handling_system_event_names.
  • Bên nhận có event handler tương ứng với eventName.
  • Payload data đúng schema mà event handler đích mong đợi.

Điểm cần lưu ý

Internal event ezy_system_event không phải là event nghiệp vụ cuối cùng. Nó chỉ là event trung gian để chuyển payload sang system endpoint.
GraphQL relay không tự validate chi tiết schema của data khi chuyển tiếp. Việc dữ liệu có hợp lệ hay không phụ thuộc vào event handler đích.
Nếu một endpoint lỗi khi gọi HTTP, relay ghi log cảnh báo và tiếp tục xử lý các endpoint khác.

Kết luận

Để gọi system event thông qua internal event, module nghiệp vụ chỉ cần phát internal event ezy_system_event với payload gồm eventNamedata. GraphQL admin plugin sẽ đảm nhiệm phần chuyển tiếp HTTP tới các system endpoint đã cấu hình, còn hệ thống nhận sẽ whitelist eventName trước khi gọi event handler thật.