Dashboard / Telemetry — Kiến trúc & dữ liệu
Chuỗi phân phối Analytics Dashboard
- Topi check permission trước khi render IFrame;
SPECIAL_BUSINESS_UNIT_IDShiện rỗng (không BU nào bị loại). - Tuke BFF bóc token từ session/header và forward JWT cho Tux; mọi endpoint Tux có
[Authorize].
Read-only, không state
- Dashboard là read model thuần: không có state transition, không phát domain events.
- Mọi mutation xảy ra ở upstream aggregates (Booking, Attendance, Account, Attendee); dashboard chỉ đọc kết quả.
- Hai sub-aggregate độc lập:
DashboardQueryContext(Staff/Admin theo BU) vàCustomerDashboardView(Customer theo account/session) — UX, data model và layer riêng.
Validation (DashboardValidationService)
| Luật | Kết quả khi vi phạm |
|---|---|
BusinessUnitId phải tồn tại + active | NOT_FOUND |
fromDate ≤ toDate | INVALID_PARAMS |
Mọi siteId thuộc BU + active | INVALID_ID (reject cả request) |
groupBy ∈ day/week/month | INVALID_PARAMS |
- Date range span mặc định 365 ngày, mở rộng tới 3650 ngày cho endpoint hiện tại.
- Tuke BFF siết
startDate ≤ endDatevà max 365 ngày — chặt hơn backend Tux (3650 ngày); gọi thẳng backend sẽ bypass giới hạn BFF.
Caching (TTL-only)
| Endpoint | TTL |
|---|---|
| TodayAttendance | 60s |
| AttendanceAnalytics | 300s |
| HourlyFlow | 600s |
| BookingStatistics (range) | 300s |
| BookingStatistics (total) | 300s |
| AccountAttendee | 300s |
| RegistrationTrend | 600s |
| SiteDistribution | 600s |
- TTL-only, không có event-driven invalidation; stale data được chấp nhận theo thiết kế.
- Cache key dài → MD5-hash để cắt độ dài;
SiteDistributionchỉ dùngtoDatetrong cache key.
Ví dụ thực tế: HourlyFlow được cache 600s, staff thấy lượng check-in trễ vài phút. → Tránh tải SQL liên tục cho widget live. → Kết quả refresh sau tối đa 10 phút, số liệu vẫn đủ dùng cho vận hành.
Site filter — đổi join
- Không có
siteIds→LEFT JOINvớiOrg_Account/Org_Attendee(account/attendee không gán site vẫn xuất hiện). - Có
siteIds→ chuyểnINNER JOIN: account/attendee ngoài tập site được chọn bị loại hẳn.
Timezone cho attendance
SignInKeyedOnlưu UTC;CheckInHourconvert về IANA timezone của BusinessUnit trước khi tách giờ (0..23).PeakStartHour/PeakEndHourdùng chung logic chuyển đổi này.- DST: spring-forward không tạo bucket giờ ảo; fall-back gộp giờ lặp lại theo quy tắc chuẩn.
Ví dụ thực tế: BU đặt timezone Pacific/Auckland, check-in 2026-03-04T20:30:00Z. → Không lệch giờ so với vận hành thực tế. → Bucket giờ 9 (giờ NZ) được dùng cho HourlyFlow và peak hour.
Telemetry (Application Insights)
- Request auth có claim user → initializer gán
AuthenticatedUserIdtừname, fallbackemail → preferred_username → upn → sub. - Gắn custom property
userNamecùng giá trị; query được qua classic (user_AuthenticatedId,customDimensions.userName) hoặc workspace (UserAuthenticatedId,Properties.userName). - Observational only: không đổi auth/routing/response; không ghi token/header/claim set/credentials.
- Không có claim phù hợp → bỏ qua
AuthenticatedUserId, request không lỗi.
Guardrails
- Dashboard không bao giờ ghi vào bảng domain khác; mọi write chỉ xảy ra ở owner aggregate.
- Mọi query phải scope theo BU active; cross-BU là lỗi boundary.
- JWT bắt buộc ở backend; permission check ở Topi trước khi render.
OnsiteRangecó thể ra âm nếuSignedOutRange > SignedInRange(data anomaly) — không có guard ở application layer (ADN-DASH-01).
Nguồn dữ liệu chính
| Nhóm | Bảng |
|---|---|
| Booking | TermBooking, TermBookingOrder, Program, ProgramCategory |
| Attendance | TermAttendance |
| Account/Attendee | BusinessUnitAccount, BusinessUnitAttendee, Org_Account, Org_Attendee |
| Site/BU | Org, BusinessUnit |