Dashboard / Telemetry — Khái niệm cốt lõi
| Khái niệm | Nghĩa |
|---|---|
| Dashboard (aggregate) | Read-only query aggregate scope theo BusinessUnit: đọc nhiều domain entity, không sở hữu state, không phát domain events. |
| DashboardQueryContext | Sub-aggregate Staff/Admin, root BusinessUnit, scope BusinessUnitId. |
| CustomerDashboardView | Sub-aggregate khách hàng, root Account, scope theo account/session. |
| Eventual consistency | Kết quả từ cache TTL-only, trễ tối đa bằng TTL của endpoint. |
| AuthenticatedUserId | User identity trong telemetry App Insights, lấy từ claim request. |
userName | Custom property telemetry, cùng giá trị AuthenticatedUserId, dùng để query. |
Hai sub-aggregate
| Sub-Aggregate | Consumer | Root | Scope |
|---|---|---|---|
DashboardQueryContext | Staff / Admin | BusinessUnit | BusinessUnitId |
CustomerDashboardView | Customer | Account | account + session |
Công thức metric
| Metric | Công thức |
|---|---|
| EstimatedRevenue | TermBooking StatusId=500 (Confirmed) + BillingId IS NOT NULL; loại 450/475. |
| TotalBookings | Cộng dồn tới toDate (không count theo range); loại 100/900; gồm TermBooking + TermBookingOrder. |
| NewBookings | Booking mới theo status-set + pending 300 (chỉ từ TermBookingOrder). |
| OnsiteRange | SignedInRange − SignedOutRange; cả hai COUNT(DISTINCT AttendeeId). |
| HourlyFlow | Attendee có SignInKeyedOn IS NOT NULL, bucket giờ 0..23 theo giờ địa phương BU. |
| PeakStartHour / PeakEndHour | Giờ cao điểm, cùng logic timezone với HourlyFlow. |
| RegistrationTrend | Tổng Accounts + Attendees theo thời gian, groupBy = day/week/month. |
| SiteDistribution | Cộng dồn Accounts + Attendees tại toDate; fromDate bị bỏ qua. |
Chi tiết quan trọng:
EstimatedRevenue:450(ConfirmedAttendance) và475(ConfirmedInvoice) không tính dù gần confirmed.TotalBookings: loại100(draft) và900(cancelled); cộng cả 2 bảng.HourlyFlow: attendee hiện diện nhưng không có record sign-in số thì không tính.
Ví dụ thực tế: Booking
StatusId=500nhưngBillingId=nullkhông vào revenue. → Chỉ booking confirm đã có hóa đơn mới tính. →EstimatedRevenueloại booking đó.
Ví dụ thực tế: BU timezone
Pacific/Auckland, trẻ sign-in20:30Z= 09:30 sáng NZ. → Không bucket theo UTC. →HourlyFlowghi vào giờ 9 giờ địa phương.
Quy tắc (tóm tắt 16 invariant)
| # | Quy tắc | Lỗi khi vi phạm |
|---|---|---|
| 001 | Mọi query scope theo BusinessUnit active. | unknown/inactive → NOT_FOUND |
| 002 | fromDate ≤ toDate. | ngược → INVALID_PARAMS |
| 003 | siteIds phải thuộc BU và active. | 1 site sai → reject cả request INVALID_ID |
| 004 | EstimatedRevenue chỉ StatusId=500 + BillingId not null. | — |
| 005 | TotalBookings cộng dồn, loại 100/900, gồm 2 bảng. | — |
| 006 | NewBookings theo status-set + pending 300 từ TermBookingOrder. | — |
| 007 | OnsiteRange = SignedInRange − SignedOutRange. | — |
| 008 | HourlyFlow chỉ count SignInKeyedOn non-null. | — |
| 009 | groupBy chỉ day/week/month. | khác → INVALID_PARAMS |
| 010 | SiteDistribution cộng dồn tại toDate; fromDate vô tác dụng. | — |
| 011 | Kết quả cache trễ tới TTL endpoint. | — |
| 012 | Endpoint dashboard yêu cầu JWT. | unauth → reject |
| 013 | Có siteIds → join account/attendee chuyển INNER JOIN. | — |
| 014 | HourlyFlow.CheckInHour theo giờ địa phương BU (IANA). | — |
| 015 | Peak hour cùng logic timezone với HourlyFlow. | — |
| 016 | DST theo quy tắc chuẩn, không tạo bucket giờ ảo. | — |
Ví dụ thực tế: Account thuộc BU nhưng chưa gán site; filter
siteIds=[2,3]. → Không được lọt vào bảng. → JoinINNER JOIN, account không xuất hiện.
Ví dụ thực tế: Staff vừa confirm booking quay lại xem widget. → Không hứa realtime. →
BookingStatisticscache TTL 300s, đúng sau tối đa 5 phút.
Ví dụ thực tế: BU New Zealand đêm chuyển xuân thiếu giờ 02:00–03:00. → Không bịa giờ không tồn tại. → Không có bucket ảo trong khoảng đó.
Telemetry user attribution
AuthenticatedUserIdtừ claimname; thiếu → fallbackemail→preferred_username→upn→sub.- Không có claim phù hợp → không ghi rỗng; request vẫn chạy.
- Gắn thêm custom property
userNamecùng giá trị.
| Loại bảng | Cách tìm username |
|---|---|
Classic (requests) | user_AuthenticatedId hoặc customDimensions.userName |
Workspace (AppRequests) | UserAuthenticatedId hoặc Properties.userName |
- Chỉ quan sát: không đổi auth/routing/response, không ghi token/credentials.
- Request lỗi auth vẫn trả response gốc.
Ví dụ thực tế: Request claim
name = "staff@example.com". → Gán cho telemetry. →AuthenticatedUserId = userName = "staff@example.com".