Dashboard / Telemetry — Architecture & data
Analytics Dashboard delivery chain
- Topi checks permissions before rendering the IFrame;
SPECIAL_BUSINESS_UNIT_IDSis currently empty (no BU is excluded). - Tuke BFF extracts the token from session/header and forwards the JWT to Tux; every Tux endpoint has
[Authorize].
Read-only, no state
- The dashboard is a pure read model: no state transitions, no domain events.
- All mutations happen in upstream aggregates (Booking, Attendance, Account, Attendee); the dashboard only reads the results.
- Two independent sub-aggregates:
DashboardQueryContext(Staff/Admin by BU) andCustomerDashboardView(Customer by account/session) — separate UX, data model, and layers.
Validation (DashboardValidationService)
| Rule | Result on violation |
|---|---|
BusinessUnitId must exist + be active | NOT_FOUND |
fromDate ≤ toDate | INVALID_PARAMS |
Every siteId belongs to the BU + active | INVALID_ID (rejects the whole request) |
groupBy ∈ day/week/month | INVALID_PARAMS |
- Default date-range span is 365 days, extendable to 3650 days for the current endpoint.
- Tuke BFF enforces
startDate ≤ endDateand a max of 365 days — stricter than the Tux backend (3650 days); calling the backend directly bypasses the BFF limit.
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, no event-driven invalidation; stale data is accepted by design.
- Long cache keys are MD5-hashed to cut length;
SiteDistributionuses onlytoDatein its cache key.
Real-world example: HourlyFlow is cached for 600s; staff see check-in counts that lag by a few minutes. → Avoid hammering SQL for a live widget. → Results refresh within at most 10 minutes, still accurate enough for operations.
Site filter — join switch
- Without
siteIds→LEFT JOINwithOrg_Account/Org_Attendee(accounts/attendees not assigned to a site still appear). - With
siteIds→ switches toINNER JOIN: accounts/attendees outside the selected site set are excluded entirely.
Timezone for attendance
SignInKeyedOnis stored in UTC;CheckInHourconverts to the BusinessUnit's IANA timezone before extracting the hour (0..23).PeakStartHour/PeakEndHourshare this conversion logic.- DST: spring-forward creates no phantom hour buckets; fall-back merges repeated hours per standard rules.
Real-world example: The BU sets timezone Pacific/Auckland; check-in 2026-03-04T20:30:00Z. → No hour mismatch with actual operations. → Hour bucket 9 (NZ time) is used for HourlyFlow and the peak hour.
Telemetry (Application Insights)
- An authenticated request with a user claim → the initializer sets
AuthenticatedUserIdfromname, fallbackemail → preferred_username → upn → sub. - Attaches the
userNamecustom property with the same value; queryable via classic (user_AuthenticatedId,customDimensions.userName) or workspace (UserAuthenticatedId,Properties.userName). - Observational only: does not change auth/routing/response; never logs token/header/claim set/credentials.
- No matching claim →
AuthenticatedUserIdis skipped; the request does not error.
Guardrails
- The dashboard never writes to other domains' tables; every write happens in the owning aggregate.
- Every query must be scoped by an active BU; cross-BU is a boundary error.
- JWT is mandatory at the backend; permission check in Topi before rendering.
OnsiteRangecan go negative ifSignedOutRange > SignedInRange(data anomaly) — no guard at the application layer (ADN-DASH-01).
Main data sources
| Group | Tables |
|---|---|
| Booking | TermBooking, TermBookingOrder, Program, ProgramCategory |
| Attendance | TermAttendance |
| Account/Attendee | BusinessUnitAccount, BusinessUnitAttendee, Org_Account, Org_Attendee |
| Site/BU | Org, BusinessUnit |